From 9cb0727880edd98a63ac3be2cfbe6fce28fda9b9 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 19 Sep 2024 22:53:44 +0900 Subject: [PATCH 001/111] test(backend): add federation test --- .github/workflows/test-federation.yml | 55 +++++++ .gitignore | 2 +- packages/backend/package.json | 1 + .../test-federation/.config/a.local.conf | 70 ++++++++ .../test-federation/.config/b.local.conf | 70 ++++++++ .../test-federation/.config/default.a.yml | 25 +++ .../test-federation/.config/default.b.yml | 25 +++ .../test-federation/.config/docker.env | 4 + packages/backend/test-federation/.env.example | 1 + packages/backend/test-federation/.gitignore | 3 + packages/backend/test-federation/README.md | 7 + .../backend/test-federation/compose.a.yml | 148 +++++++++++++++++ .../backend/test-federation/compose.b.yml | 151 ++++++++++++++++++ .../test-federation/compose.override.yaml | 99 ++++++++++++ packages/backend/test-federation/compose.yml | 111 +++++++++++++ packages/backend/test-federation/daemon.ts | 36 +++++ .../backend/test-federation/eslint.config.js | 22 +++ .../test-federation/generate_certificates.sh | 32 ++++ .../test-federation/test/drive.test.ts | 97 +++++++++++ .../backend/test-federation/test/user.test.ts | 129 +++++++++++++++ .../backend/test-federation/test/utils.ts | 137 ++++++++++++++++ .../backend/test-federation/tsconfig.json | 114 +++++++++++++ 22 files changed, 1338 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/test-federation.yml create mode 100644 packages/backend/test-federation/.config/a.local.conf create mode 100644 packages/backend/test-federation/.config/b.local.conf create mode 100644 packages/backend/test-federation/.config/default.a.yml create mode 100644 packages/backend/test-federation/.config/default.b.yml create mode 100644 packages/backend/test-federation/.config/docker.env create mode 100644 packages/backend/test-federation/.env.example create mode 100644 packages/backend/test-federation/.gitignore create mode 100644 packages/backend/test-federation/README.md create mode 100644 packages/backend/test-federation/compose.a.yml create mode 100644 packages/backend/test-federation/compose.b.yml create mode 100644 packages/backend/test-federation/compose.override.yaml create mode 100644 packages/backend/test-federation/compose.yml create mode 100644 packages/backend/test-federation/daemon.ts create mode 100644 packages/backend/test-federation/eslint.config.js create mode 100644 packages/backend/test-federation/generate_certificates.sh create mode 100644 packages/backend/test-federation/test/drive.test.ts create mode 100644 packages/backend/test-federation/test/user.test.ts create mode 100644 packages/backend/test-federation/test/utils.ts create mode 100644 packages/backend/test-federation/tsconfig.json diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml new file mode 100644 index 000000000000..9ca28fe328df --- /dev/null +++ b/.github/workflows/test-federation.yml @@ -0,0 +1,55 @@ +name: Test (federation) + +on: + push: + branches: + - master + - develop + paths: + - packages/backend/** + - packages/misskey-js/** + - .github/workflows/test-federation.yml + pull_request: + paths: + - packages/backend/** + - packages/misskey-js/** + - .github/workflows/test-federation.yml + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20.16.0] + steps: + - uses: actions/checkout@v4 + - name: Install FFmpeg + uses: FedericoCarboni/setup-ffmpeg@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4.0.3 + with: + node-version: ${{ matrix.node-version }} + cache: 'pnpm' + - name: Build Misskey + run: | + corepack enable && corepack prepare + pnpm -F misskey-js i --frozen-lockfile + pnpm -F misskey-js build + pnpm -F misskey-reversi i --frozen-lockfile + pnpm -F misskey-reversi build + pnpm -F backend i --frozen-lockfile + pnpm -F backend build + pnpm -F backend build:fed + - name: Setup + run: | + cd packages/backend/test-federation + cp ./.env.example ./.env + bash ./generate_certificates.sh + sudo chmod 644 ./certificates/*.local.key + - name: Start servers + # https://github.com/docker/compose/issues/1294#issuecomment-374847206 + run: docker compose up -d --scale tester=0 + - name: Test + run: docker compose run tester + - name: Stop servers + run: docker compose down diff --git a/.gitignore b/.gitignore index b270d5cb3a36..5b8a798ba64f 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,7 @@ coverage !/.config/docker_example.env !/.config/cypress-devcontainer.yml docker-compose.yml -compose.yml +./compose.yml .devcontainer/compose.yml !/.devcontainer/compose.yml diff --git a/packages/backend/package.json b/packages/backend/package.json index a06fd9156b7a..55452d6539e6 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -16,6 +16,7 @@ "build:test": "swc test-server -d built-test -D --config-file test-server/.swcrc --strip-leading-paths", "watch:swc": "swc src -d built -D -w --strip-leading-paths", "build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json", + "build:fed": "cd test-federation && tsc -p tsconfig.json", "watch": "node ./scripts/watch.mjs", "restart": "pnpm build && pnpm start", "dev": "node ./scripts/dev.mjs", diff --git a/packages/backend/test-federation/.config/a.local.conf b/packages/backend/test-federation/.config/a.local.conf new file mode 100644 index 000000000000..cbb65b6e057f --- /dev/null +++ b/packages/backend/test-federation/.config/a.local.conf @@ -0,0 +1,70 @@ +# based on https://github.com/misskey-dev/misskey-hub/blob/7071f63a1c80ee35c71f0fd8a6d8722c118c7574/src/docs/admin/nginx.md + +# For WebSocket +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=cache1:16m max_size=1g inactive=720m use_temp_path=off; + +server { + listen 80; + listen [::]:80; + server_name a.local; + + # For SSL domain validation + root /var/www/html; + location /.well-known/acme-challenge/ { allow all; } + location /.well-known/pki-validation/ { allow all; } + location / { return 301 https://$server_name$request_uri; } +} + +server { + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + server_name a.local; + + ssl_session_timeout 1d; + ssl_session_cache shared:ssl_session_cache:10m; + ssl_session_tickets off; + + ssl_trusted_certificate /etc/nginx/certificates/rootCA.crt; + ssl_certificate /etc/nginx/certificates/$server_name.crt; + ssl_certificate_key /etc/nginx/certificates/$server_name.key; + + # SSL protocol settings + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_stapling on; + ssl_stapling_verify on; + + # Change to your upload limit + client_max_body_size 80m; + + # Proxy to Node + location / { + proxy_pass http://misskey.a.local:3000; + proxy_set_header Host $host; + proxy_http_version 1.1; + proxy_redirect off; + + # If it's behind another reverse proxy or CDN, remove the following. + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + + # For WebSocket + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + # Cache settings + proxy_cache cache1; + proxy_cache_lock on; + proxy_cache_use_stale updating; + proxy_force_ranges on; + add_header X-Cache $upstream_cache_status; + } +} diff --git a/packages/backend/test-federation/.config/b.local.conf b/packages/backend/test-federation/.config/b.local.conf new file mode 100644 index 000000000000..380b2ceef328 --- /dev/null +++ b/packages/backend/test-federation/.config/b.local.conf @@ -0,0 +1,70 @@ +# based on https://github.com/misskey-dev/misskey-hub/blob/7071f63a1c80ee35c71f0fd8a6d8722c118c7574/src/docs/admin/nginx.md + +# For WebSocket +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=cache1:16m max_size=1g inactive=720m use_temp_path=off; + +server { + listen 80; + listen [::]:80; + server_name b.local; + + # For SSL domain validation + root /var/www/html; + location /.well-known/acme-challenge/ { allow all; } + location /.well-known/pki-validation/ { allow all; } + location / { return 301 https://$server_name$request_uri; } +} + +server { + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + server_name b.local; + + ssl_session_timeout 1d; + ssl_session_cache shared:ssl_session_cache:10m; + ssl_session_tickets off; + + ssl_trusted_certificate /etc/nginx/certificates/rootCA.crt; + ssl_certificate /etc/nginx/certificates/$server_name.crt; + ssl_certificate_key /etc/nginx/certificates/$server_name.key; + + # SSL protocol settings + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_stapling on; + ssl_stapling_verify on; + + # Change to your upload limit + client_max_body_size 80m; + + # Proxy to Node + location / { + proxy_pass http://misskey.b.local:3000; + proxy_set_header Host $host; + proxy_http_version 1.1; + proxy_redirect off; + + # If it's behind another reverse proxy or CDN, remove the following. + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + + # For WebSocket + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + # Cache settings + proxy_cache cache1; + proxy_cache_lock on; + proxy_cache_use_stale updating; + proxy_force_ranges on; + add_header X-Cache $upstream_cache_status; + } +} diff --git a/packages/backend/test-federation/.config/default.a.yml b/packages/backend/test-federation/.config/default.a.yml new file mode 100644 index 000000000000..9a1456afdefd --- /dev/null +++ b/packages/backend/test-federation/.config/default.a.yml @@ -0,0 +1,25 @@ +url: https://a.local/ +port: 3000 +db: + host: db.a.local + port: 5432 + db: misskey + user: postgres + pass: postgres +dbReplications: false +redis: + host: redis.local + port: 6379 +id: 'aidx' +proxyBypassHosts: + - api.deepl.com + - api-free.deepl.com + - www.recaptcha.net + - hcaptcha.com + - challenges.cloudflare.com +proxyRemoteFiles: true +signToActivityPubGet: true +allowedPrivateNetworks: [ + '127.0.0.1/32', + '172.20.0.0/16' +] diff --git a/packages/backend/test-federation/.config/default.b.yml b/packages/backend/test-federation/.config/default.b.yml new file mode 100644 index 000000000000..0785cd7391d8 --- /dev/null +++ b/packages/backend/test-federation/.config/default.b.yml @@ -0,0 +1,25 @@ +url: https://b.local/ +port: 3000 +db: + host: db.b.local + port: 5432 + db: misskey + user: postgres + pass: postgres +dbReplications: false +redis: + host: redis.local + port: 6379 +id: 'aidx' +proxyBypassHosts: + - api.deepl.com + - api-free.deepl.com + - www.recaptcha.net + - hcaptcha.com + - challenges.cloudflare.com +proxyRemoteFiles: true +signToActivityPubGet: true +allowedPrivateNetworks: [ + '127.0.0.1/32', + '172.20.0.0/16' +] diff --git a/packages/backend/test-federation/.config/docker.env b/packages/backend/test-federation/.config/docker.env new file mode 100644 index 000000000000..b2a0177c84ca --- /dev/null +++ b/packages/backend/test-federation/.config/docker.env @@ -0,0 +1,4 @@ +NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/rootCA.crt +POSTGRES_DB=misskey +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres diff --git a/packages/backend/test-federation/.env.example b/packages/backend/test-federation/.env.example new file mode 100644 index 000000000000..97ec4d359404 --- /dev/null +++ b/packages/backend/test-federation/.env.example @@ -0,0 +1 @@ +TESTER_IP_ADDRESS=172.20.1.1 diff --git a/packages/backend/test-federation/.gitignore b/packages/backend/test-federation/.gitignore new file mode 100644 index 000000000000..dd03bf2cab20 --- /dev/null +++ b/packages/backend/test-federation/.gitignore @@ -0,0 +1,3 @@ +certificates +volumes +.env diff --git a/packages/backend/test-federation/README.md b/packages/backend/test-federation/README.md new file mode 100644 index 000000000000..c69a0c611ff4 --- /dev/null +++ b/packages/backend/test-federation/README.md @@ -0,0 +1,7 @@ +Execute following commands: +```sh +cp ./.env.example ./.env +bash ./generate_certificates.sh +pnpm build:fed +docker compose up +``` diff --git a/packages/backend/test-federation/compose.a.yml b/packages/backend/test-federation/compose.a.yml new file mode 100644 index 000000000000..3046c166dfb8 --- /dev/null +++ b/packages/backend/test-federation/compose.a.yml @@ -0,0 +1,148 @@ +services: + a.local: + image: nginx:1.27 + depends_on: + misskey.a.local: + condition: service_healthy + networks: + - internal_network_a + volumes: + - type: bind + source: ./.config/a.local.conf + target: /etc/nginx/conf.d/a.local.conf + read_only: true + - type: bind + source: ./certificates/a.local.crt + target: /etc/nginx/certificates/a.local.crt + read_only: true + - type: bind + source: ./certificates/a.local.key + target: /etc/nginx/certificates/a.local.key + read_only: true + - type: bind + source: ./certificates/rootCA.crt + target: /etc/nginx/certificates/rootCA.crt + read_only: true + healthcheck: + test: service nginx status + interval: 5s + retries: 20 + + misskey.a.local: + image: node:20 + depends_on: + db.a.local: + condition: service_healthy + redis.local: + condition: service_healthy + networks: + - internal_network_a + env_file: + - ./.config/docker.env + environment: + - NODE_ENV=production + volumes: + - type: bind + source: ./.config/default.a.yml + target: /misskey/.config/default.yml + read_only: true + - type: bind + source: ../../../built + target: /misskey/built + read_only: true + - type: bind + source: ../assets + target: /misskey/packages/backend/assets + read_only: true + - type: bind + source: ../built + target: /misskey/packages/backend/built + read_only: true + - type: bind + source: ../migration + target: /misskey/packages/backend/migration + read_only: true + - type: bind + source: ../ormconfig.js + target: /misskey/packages/backend/ormconfig.js + read_only: true + - type: bind + source: ../package.json + target: /misskey/packages/backend/package.json + read_only: true + - type: bind + source: ../../misskey-js/built + target: /misskey/packages/misskey-js/built + read_only: true + - type: bind + source: ../../misskey-js/package.json + target: /misskey/packages/misskey-js/package.json + read_only: true + - type: bind + source: ../../misskey-reversi/built + target: /misskey/packages/misskey-reversi/built + read_only: true + - type: bind + source: ../../misskey-reversi/package.json + target: /misskey/packages/misskey-reversi/package.json + read_only: true + - type: bind + source: ../../../healthcheck.sh + target: /misskey/healthcheck.sh + read_only: true + - type: bind + source: ../../../package.json + target: /misskey/package.json + read_only: true + - type: bind + source: ../../../pnpm-lock.yaml + target: /misskey/pnpm-lock.yaml + read_only: true + - type: bind + source: ../../../pnpm-workspace.yaml + target: /misskey/pnpm-workspace.yaml + read_only: true + - type: bind + source: ./certificates/rootCA.crt + target: /usr/local/share/ca-certificates/rootCA.crt + read_only: true + working_dir: /misskey + command: > + bash -c " + corepack enable && corepack prepare + pnpm -F backend i + pnpm -F misskey-js i + pnpm -F misskey-reversi i + pnpm -F backend migrate + pnpm -F backend start + " + healthcheck: + test: bash /misskey/healthcheck.sh + interval: 5s + retries: 20 + + db.a.local: + image: postgres:15-alpine + networks: + - internal_network_a + env_file: + - ./.config/docker.env + volumes: + - type: bind + source: ./volumes/db.a + target: /var/lib/postgresql/data + bind: + create_host_path: true + healthcheck: + test: pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB + interval: 5s + retries: 20 + +networks: + internal_network_a: + internal: true + driver: bridge + ipam: + config: + - subnet: 172.21.0.0/16 + ip_range: 172.21.0.0/24 diff --git a/packages/backend/test-federation/compose.b.yml b/packages/backend/test-federation/compose.b.yml new file mode 100644 index 000000000000..7dddac905c21 --- /dev/null +++ b/packages/backend/test-federation/compose.b.yml @@ -0,0 +1,151 @@ +services: + b.local: + image: nginx:1.27 + depends_on: + misskey.b.local: + condition: service_healthy + networks: + - internal_network_b + volumes: + - type: bind + source: ./.config/b.local.conf + target: /etc/nginx/conf.d/b.local.conf + read_only: true + - type: bind + source: ./certificates/b.local.crt + target: /etc/nginx/certificates/b.local.crt + read_only: true + - type: bind + source: ./certificates/b.local.key + target: /etc/nginx/certificates/b.local.key + read_only: true + - type: bind + source: ./certificates/rootCA.crt + target: /etc/nginx/certificates/rootCA.crt + read_only: true + healthcheck: + test: service nginx status + interval: 5s + retries: 20 + + misskey.b.local: + image: node:20 + depends_on: + db.b.local: + condition: service_healthy + redis.local: + condition: service_healthy + # avoid conflict for installing dependencies + misskey.a.local: + condition: service_healthy + networks: + - internal_network_b + env_file: + - ./.config/docker.env + environment: + - NODE_ENV=production + volumes: + - type: bind + source: ./.config/default.b.yml + target: /misskey/.config/default.yml + read_only: true + - type: bind + source: ../../../built + target: /misskey/built + read_only: true + - type: bind + source: ../assets + target: /misskey/packages/backend/assets + read_only: true + - type: bind + source: ../built + target: /misskey/packages/backend/built + read_only: true + - type: bind + source: ../migration + target: /misskey/packages/backend/migration + read_only: true + - type: bind + source: ../ormconfig.js + target: /misskey/packages/backend/ormconfig.js + read_only: true + - type: bind + source: ../package.json + target: /misskey/packages/backend/package.json + read_only: true + - type: bind + source: ../../misskey-js/built + target: /misskey/packages/misskey-js/built + read_only: true + - type: bind + source: ../../misskey-js/package.json + target: /misskey/packages/misskey-js/package.json + read_only: true + - type: bind + source: ../../misskey-reversi/built + target: /misskey/packages/misskey-reversi/built + read_only: true + - type: bind + source: ../../misskey-reversi/package.json + target: /misskey/packages/misskey-reversi/package.json + read_only: true + - type: bind + source: ../../../healthcheck.sh + target: /misskey/healthcheck.sh + read_only: true + - type: bind + source: ../../../package.json + target: /misskey/package.json + read_only: true + - type: bind + source: ../../../pnpm-lock.yaml + target: /misskey/pnpm-lock.yaml + read_only: true + - type: bind + source: ../../../pnpm-workspace.yaml + target: /misskey/pnpm-workspace.yaml + read_only: true + - type: bind + source: ./certificates/rootCA.crt + target: /usr/local/share/ca-certificates/rootCA.crt + read_only: true + working_dir: /misskey + command: > + bash -c " + corepack enable && corepack prepare + pnpm -F backend i + pnpm -F misskey-js i + pnpm -F misskey-reversi i + pnpm -F backend migrate + pnpm -F backend start + " + healthcheck: + test: bash /misskey/healthcheck.sh + interval: 5s + retries: 20 + + db.b.local: + image: postgres:15-alpine + networks: + - internal_network_b + env_file: + - ./.config/docker.env + volumes: + - type: bind + source: ./volumes/db.b + target: /var/lib/postgresql/data + bind: + create_host_path: true + healthcheck: + test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB" + interval: 5s + retries: 20 + +networks: + internal_network_b: + internal: true + driver: bridge + ipam: + config: + - subnet: 172.22.0.0/16 + ip_range: 172.22.0.0/24 diff --git a/packages/backend/test-federation/compose.override.yaml b/packages/backend/test-federation/compose.override.yaml new file mode 100644 index 000000000000..a35b540ca7d8 --- /dev/null +++ b/packages/backend/test-federation/compose.override.yaml @@ -0,0 +1,99 @@ +services: + tester: + networks: + external_network: + internal_network: + ipv4_address: $TESTER_IP_ADDRESS + volumes: + - type: volume + source: node_modules + target: /misskey/node_modules + - type: volume + source: node_modules_backend + target: /misskey/packages/backend/node_modules + - type: volume + source: node_modules_misskey-js + target: /misskey/packages/misskey-js/node_modules + + daemon: + networks: + - external_network + - internal_network_a + - internal_network_b + volumes: + - type: volume + source: node_modules + target: /misskey/node_modules + - type: volume + source: node_modules_backend + target: /misskey/packages/backend/node_modules + + redis.local: + networks: + - internal_network_a + - internal_network_b + + a.local: + networks: + - internal_network + + misskey.a.local: + networks: + - external_network + - internal_network + volumes: + - type: volume + source: node_modules + target: /misskey/node_modules + - type: volume + source: node_modules_backend + target: /misskey/packages/backend/node_modules + - type: volume + source: node_modules_misskey-js + target: /misskey/packages/misskey-js/node_modules + - type: volume + source: node_modules_misskey-reversi + target: /misskey/packages/misskey-reversi/node_modules + + b.local: + networks: + - internal_network + + misskey.b.local: + networks: + - external_network + - internal_network + volumes: + - type: volume + source: node_modules + target: /misskey/node_modules + - type: volume + source: node_modules_backend + target: /misskey/packages/backend/node_modules + - type: volume + source: node_modules_misskey-js + target: /misskey/packages/misskey-js/node_modules + - type: volume + source: node_modules_misskey-reversi + target: /misskey/packages/misskey-reversi/node_modules + +networks: + external_network: + driver: bridge + ipam: + config: + - subnet: 172.23.0.0/16 + ip_range: 172.23.0.0/24 + internal_network: + internal: true + driver: bridge + ipam: + config: + - subnet: 172.20.0.0/16 + ip_range: 172.20.0.0/24 + +volumes: + node_modules: + node_modules_backend: + node_modules_misskey-js: + node_modules_misskey-reversi: diff --git a/packages/backend/test-federation/compose.yml b/packages/backend/test-federation/compose.yml new file mode 100644 index 000000000000..27a018847fd6 --- /dev/null +++ b/packages/backend/test-federation/compose.yml @@ -0,0 +1,111 @@ +include: + - ./compose.a.yml + - ./compose.b.yml + +services: + tester: + image: node:20 + depends_on: + a.local: + condition: service_healthy + b.local: + condition: service_healthy + environment: + - NODE_ENV=production + - NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/rootCA.crt + volumes: + - type: bind + source: ../package.json + target: /misskey/packages/backend/package.json + read_only: true + - type: bind + source: ../test/resources + target: /misskey/packages/backend/test/resources + read_only: true + - type: bind + source: ./built + target: /misskey/packages/backend/test-federation + read_only: true + - type: bind + source: ../../misskey-js/built + target: /misskey/packages/misskey-js/built + read_only: true + - type: bind + source: ../../misskey-js/package.json + target: /misskey/packages/misskey-js/package.json + read_only: true + - type: bind + source: ../../../package.json + target: /misskey/package.json + read_only: true + - type: bind + source: ../../../pnpm-lock.yaml + target: /misskey/pnpm-lock.yaml + read_only: true + - type: bind + source: ../../../pnpm-workspace.yaml + target: /misskey/pnpm-workspace.yaml + read_only: true + - type: bind + source: ./certificates/rootCA.crt + target: /usr/local/share/ca-certificates/rootCA.crt + read_only: true + working_dir: /misskey + command: > + bash -c " + corepack enable && corepack prepare + pnpm -F backend i + node --test "./packages/backend/test-federation/test/*.test.js" + " + + daemon: + image: node:20 + depends_on: + misskey.a.local: + condition: service_healthy + misskey.b.local: + condition: service_healthy + environment: + - NODE_ENV=production + - TESTER_IP_ADDRESS=$TESTER_IP_ADDRESS + volumes: + - type: bind + source: ../package.json + target: /misskey/packages/backend/package.json + read_only: true + - type: bind + source: ./built + target: /misskey/packages/backend/test-federation + read_only: true + - type: bind + source: ../../../package.json + target: /misskey/package.json + read_only: true + - type: bind + source: ../../../pnpm-lock.yaml + target: /misskey/pnpm-lock.yaml + read_only: true + - type: bind + source: ../../../pnpm-workspace.yaml + target: /misskey/pnpm-workspace.yaml + read_only: true + working_dir: /misskey + command: > + bash -c " + corepack enable && corepack prepare + pnpm -F backend i + node ./packages/backend/test-federation/daemon.js + " + + redis.local: + image: redis:7-alpine + volumes: + - type: bind + source: ./volumes/redis + target: /data + bind: + create_host_path: true + healthcheck: + test: redis-cli ping + interval: 5s + retries: 20 diff --git a/packages/backend/test-federation/daemon.ts b/packages/backend/test-federation/daemon.ts new file mode 100644 index 000000000000..8eb275d4c3b8 --- /dev/null +++ b/packages/backend/test-federation/daemon.ts @@ -0,0 +1,36 @@ +import IPCIDR from 'ip-cidr'; +import { Redis } from 'ioredis'; + +/** + * This should be same as {@link file://./../src/misc/get-ip-hash.ts}. + */ +function getIpHash(ip: string) { + const prefix = IPCIDR.createAddress(ip).mask(64); + return `ip-${BigInt('0b' + prefix).toString(36)}`; +} + +/** + * This prevents hitting rate limit when login. + */ +export async function purgeLimit(host: string, client: Redis) { + const ipHash = getIpHash(process.env.TESTER_IP_ADDRESS!); + const key = `${host}:limit:${ipHash}:signin`; + const res = await client.zrange(key, 0, -1); + if (res.length !== 0) { + console.log(`${key} - ${JSON.stringify(res)}`); + await client.del(key); + } +} + +console.log('Daemon started running'); + +{ + const redisClient = new Redis({ + host: 'redis.local', + }); + + setInterval(() => { + purgeLimit('a.local', redisClient); + purgeLimit('b.local', redisClient); + }, 1000); +} diff --git a/packages/backend/test-federation/eslint.config.js b/packages/backend/test-federation/eslint.config.js new file mode 100644 index 000000000000..a0f43babadfb --- /dev/null +++ b/packages/backend/test-federation/eslint.config.js @@ -0,0 +1,22 @@ +import globals from 'globals'; +import tsParser from '@typescript-eslint/parser'; +import sharedConfig from '../../shared/eslint.config.js'; + +export default [ + ...sharedConfig, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + globals: { + ...globals.node, + ...globals.jest, + }, + parserOptions: { + parser: tsParser, + project: ['./tsconfig.json'], + sourceType: 'module', + tsconfigRootDir: import.meta.dirname, + }, + }, + }, +]; diff --git a/packages/backend/test-federation/generate_certificates.sh b/packages/backend/test-federation/generate_certificates.sh new file mode 100644 index 000000000000..eae68ba83907 --- /dev/null +++ b/packages/backend/test-federation/generate_certificates.sh @@ -0,0 +1,32 @@ +#!/bin/bash +mkdir certificates + +# rootCA +openssl genrsa -des3 \ + -passout pass:rootCA \ + -out certificates/rootCA.key 4096 +openssl req -x509 -new -nodes -batch \ + -key certificates/rootCA.key \ + -sha256 \ + -days 1024 \ + -passin pass:rootCA \ + -out certificates/rootCA.crt + +# domain +function generate { + openssl req -new -newkey rsa:2048 -sha256 -nodes \ + -keyout certificates/$1.key \ + -subj "/CN=$1/emailAddress=admin@$1/C=JP/ST=/L=/O=Misskey Tester/OU=Some Unit" \ + -out certificates/$1.csr + openssl x509 -req -sha256 \ + -in certificates/$1.csr \ + -CA certificates/rootCA.crt \ + -CAkey certificates/rootCA.key \ + -CAcreateserial \ + -passin pass:rootCA \ + -out certificates/$1.crt \ + -days 500 +} + +generate a.local +generate b.local diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts new file mode 100644 index 000000000000..892d75488b7d --- /dev/null +++ b/packages/backend/test-federation/test/drive.test.ts @@ -0,0 +1,97 @@ +import { deepEqual, deepStrictEqual, strictEqual } from 'node:assert'; +import test, { describe } from 'node:test'; +import * as Misskey from 'misskey-js'; +import { createAccount, fetchAdmin, uploadFile } from './utils.js'; + +const [ + [, aAdminClient], + [, bAdminClient], +] = await Promise.all([ + fetchAdmin('a.local'), + fetchAdmin('b.local'), +]); + +describe('Drive', () => { + describe('Upload image in a.local and resolve from b.local', async () => { + const [uploader, uploaderClient] = await createAccount('a.local', aAdminClient); + + const image = await uploadFile('a.local', '../../test/resources/192.jpg', uploader.i); + const noteWithImage = (await uploaderClient.request('notes/create', { fileIds: [image.id] })).createdNote; + const uri = `https://a.local/notes/${noteWithImage.id}`; + const noteInBServer = await (async (): Promise => { + const resolved = await bAdminClient.request('ap/show', { uri }); + strictEqual(resolved.type, 'Note'); + return resolved; + })(); + deepEqual(noteInBServer.object.uri, uri); + deepEqual(noteInBServer.object.files != null, true); + deepEqual(noteInBServer.object.files!.length, 1); + const imageInBServer = noteInBServer.object.files![0]; + + await test('Check consistency of DriveFile', () => { + // console.log(`a.local: ${JSON.stringify(image, null, '\t')}`); + // console.log(`b.local: ${JSON.stringify(imageInBServer, null, '\t')}`); + + const toBeDeleted: (keyof Misskey.entities.DriveFile)[] = [ + 'id', + 'createdAt', + 'size', + 'url', + 'thumbnailUrl', + 'userId', + ]; + const _Image: Partial = structuredClone(image); + const _ImageInBServer: Partial = structuredClone(imageInBServer); + + for (const image of [_Image, _ImageInBServer]) { + for (const field of toBeDeleted) { + delete image[field]; + } + } + + deepStrictEqual(_Image, _ImageInBServer); + }); + + const updatedImage = await uploaderClient.request('drive/files/update', { + fileId: image.id, + name: 'updated_192.jpg', + isSensitive: true, + }); + + const updatedImageInBServer = await bAdminClient.request('drive/files/show', { + fileId: imageInBServer.id, + }); + + await test('Update', async () => { + // console.log(`a.local: ${JSON.stringify(updatedImage, null, '\t')}`); + // console.log(`b.local: ${JSON.stringify(updatedImageInBServer, null, '\t')}`); + + // FIXME: not updated with `drive/files/update` + deepEqual(updatedImage.isSensitive, true); + deepEqual(updatedImage.name, 'updated_192.jpg'); + deepEqual(updatedImageInBServer.isSensitive, false); + deepEqual(updatedImageInBServer.name, '192.jpg'); + }); + + const noteWithUpdatedImage = (await uploaderClient.request('notes/create', { fileIds: [updatedImage.id] })).createdNote; + const uriUpdated = `https://a.local/notes/${noteWithUpdatedImage.id}`; + const noteWithUpdatedImageInBServer = await (async (): Promise => { + const resolved = await bAdminClient.request('ap/show', { uri: uriUpdated }); + strictEqual(resolved.type, 'Note'); + return resolved; + })(); + deepEqual(noteWithUpdatedImageInBServer.object.uri, uriUpdated); + deepEqual(noteWithUpdatedImageInBServer.object.files != null, true); + deepEqual(noteWithUpdatedImageInBServer.object.files!.length, 1); + const reupdatedImageInBServer = noteWithUpdatedImageInBServer.object.files![0]; + + await test('Re-update with attaching to Note', async () => { + // console.log(`b.local: ${JSON.stringify(reupdatedImageInBServer, null, '\t')}`); + + // `isSensitive` is updated + deepEqual(reupdatedImageInBServer.isSensitive, true); + // FIXME: but `name` is not updated + deepEqual(reupdatedImageInBServer.name, '192.jpg'); + }); + }); +}); diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts new file mode 100644 index 000000000000..1e691d18522c --- /dev/null +++ b/packages/backend/test-federation/test/user.test.ts @@ -0,0 +1,129 @@ +import { deepEqual, deepStrictEqual, strictEqual } from 'node:assert'; +import test, { before, describe } from 'node:test'; +import * as Misskey from 'misskey-js'; +import { createAccount, fetchAdmin, resolveRemoteAccount } from './utils.js'; + +const [ + [, aAdminClient], + [, bAdminClient], +] = await Promise.all([ + fetchAdmin('a.local'), + fetchAdmin('b.local'), +]); + +describe('User', () => { + describe('Profile', async () => { + describe('Consistency of profile', async () => { + const [alice] = await createAccount('a.local', aAdminClient); + const [ + [, aliceWatcherClient], + [, aliceWatcherInBServerClient], + ] = await Promise.all([ + createAccount('a.local', aAdminClient), + createAccount('b.local', bAdminClient), + ]); + + const aliceInAServer = await aliceWatcherClient.request('users/show', { userId: alice.id }); + + const resolved = await (async (): Promise => { + const resolved = await aliceWatcherInBServerClient.request('ap/show', { + uri: `https://a.local/@${aliceInAServer.username}`, + }); + strictEqual(resolved.type, 'User'); + return resolved; + })(); + + const aliceInBServer = await aliceWatcherInBServerClient.request('users/show', { userId: resolved.object.id }); + + // console.log(`a.local: ${JSON.stringify(aliceInAServer, null, '\t')}`); + // console.log(`b.local: ${JSON.stringify(aliceInBServer, null, '\t')}`); + + const toBeDeleted: (keyof Misskey.entities.UserDetailedNotMe)[] = [ + 'id', + 'host', + 'avatarUrl', + 'instance', + 'badgeRoles', + 'url', + 'uri', + 'createdAt', + 'lastFetchedAt', + 'publicReactions', + ]; + const _aliceInAServer: Partial = structuredClone(aliceInAServer); + const _aliceInBServer: Partial = structuredClone(aliceInBServer); + for (const alice of [_aliceInAServer, _aliceInBServer]) { + for (const field of toBeDeleted) { + delete alice[field]; + } + } + + deepStrictEqual(_aliceInAServer, _aliceInBServer); + }); + }); + + describe('Follow / Unfollow', async () => { + const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); + const [bob, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + + const aliceAcct = `@${aliceUsername}@a.local`; + const bobAcct = `@${bobUsername}@b.local`; + + const [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteAccount(aliceAcct, bobAcct, aliceClient), + resolveRemoteAccount(bobAcct, aliceAcct, bobClient), + ]); + + await describe('Follow a.local ==> b.local', async () => { + before(async () => { + console.log(`Following ${bobAcct} from ${aliceAcct} ...`); + await aliceClient.request('following/create', { userId: bobInAServer.object.id }); + console.log(`Followed ${bobAcct} from ${aliceAcct}`); + + // wait for 1 secound + await new Promise(resolve => setTimeout(resolve, 1000)); + }); + + test('Check consistency with `users/following` and `users/followers` endpoints', async () => { + await Promise.all([ + deepEqual( + (await aliceClient.request('users/following', { userId: alice.id })) + .some(v => v.followeeId === bobInAServer.object.id), + true, + ), + deepEqual( + (await bobClient.request('users/followers', { userId: bob.id })) + .some(v => v.followerId === aliceInBServer.object.id), + true, + ), + ]); + }); + }); + + await describe('Unfollow a.local ==> b.local', async () => { + before(async () => { + console.log(`Unfollowing ${bobAcct} from ${aliceAcct} ...`); + await aliceClient.request('following/delete', { userId: bobInAServer.object.id }); + console.log(`Unfollowed ${bobAcct} from ${aliceAcct}`); + + // wait for 1 secound + await new Promise(resolve => setTimeout(resolve, 1000)); + }); + + test('Check consistency with `users/following` and `users/followers` endpoints', async () => { + await Promise.all([ + deepEqual( + (await aliceClient.request('users/following', { userId: alice.id })) + .some(v => v.followeeId === bobInAServer.object.id), + false, + ), + deepEqual( + (await bobClient.request('users/followers', { userId: bob.id })) + .some(v => v.followerId === aliceInBServer.object.id), + false, + ), + ]); + }); + }); + }); +}); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts new file mode 100644 index 000000000000..77a915ae35c5 --- /dev/null +++ b/packages/backend/test-federation/test/utils.ts @@ -0,0 +1,137 @@ +import { strictEqual } from 'assert'; +import { readFile } from 'fs/promises'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; +import * as Misskey from 'misskey-js'; +import { SwitchCaseResponseType } from 'misskey-js/api.types.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +/** used for avoiding overload and some endpoints */ +type Request = ( + endpoint: E, params: P, credential?: string | null +) => Promise>; + +export const ADMIN_PARAMS = { username: 'admin', password: 'admin' }; + +export async function signin(host: string, params: Misskey.entities.SigninRequest): Promise { + // wait for a second to prevent hit rate limit + await new Promise(resolve => setTimeout(resolve, 1000)); + console.log(`Sign in to @${params.username}@${host} ...`); + return await (new Misskey.api.APIClient({ + origin: `https://${host}`, + fetch: (input, init) => fetch(input, { + ...init, + headers: { + ...init?.headers, + 'Content-Type': init?.headers['Content-Type'] != null ? init.headers['Content-Type'] : 'application/json', + }, + }), + }).request as Request)('signin', params) + .then(res => { + console.log(`Signed in to @${params.username}@${host}`); + return res; + }) + .catch(async err => { + if (err.id === '22d05606-fbcf-421a-a2db-b32610dcfd1b') { + await new Promise(resolve => setTimeout(resolve, Math.random() * 5000)); + return await signin(host, params); + } + throw err; + }); +} + +async function createAdmin(host: string): Promise { + const client = new Misskey.api.APIClient({ origin: `https://${host}` }); + return await client.request('admin/accounts/create', ADMIN_PARAMS).then(res => { + console.log(`Successfully created admin account: @${ADMIN_PARAMS.username}@${host}`); + return res as Misskey.entities.SignupResponse; + }).then(async res => { + await client.request('admin/roles/update-default-policies', { + policies: { + rateLimitFactor: 0 as never, + }, + }, res.token); + return res; + }).catch(err => { + if (err.info.e.message === 'access denied') { + console.log(`Admin account already exists: @${ADMIN_PARAMS.username}@${host}`); + return undefined; + } + throw err; + }); +} + +export async function fetchAdmin(host: string): Promise<[Misskey.entities.SigninResponse, Misskey.api.APIClient]> { + const admin = await signin(host, ADMIN_PARAMS) + .catch(async err => { + if (err.id === '6cc579cc-885d-43d8-95c2-b8c7fc963280') { + await createAdmin(host); + return await signin(host, ADMIN_PARAMS); + } else if (err.id === '22d05606-fbcf-421a-a2db-b32610dcfd1b') { + return await signin(host, ADMIN_PARAMS); + } + throw err; + }); + + return [admin, new Misskey.api.APIClient({ origin: `https://${host}`, credential: admin.i })]; +} + +export async function createAccount(host: string, adminClient: Misskey.api.APIClient): Promise<[Misskey.entities.SigninResponse, Misskey.api.APIClient, { username: string; password: string }]> { + const username = crypto.randomUUID().replaceAll('-', '').substring(0, 20); + const password = crypto.randomUUID().replaceAll('-', ''); + await adminClient.request('admin/accounts/create', { username, password }); + console.log(`Created an account: @${username}@${host}`); + const signinRes = await signin(host, { username, password }); + + return [ + signinRes, + new Misskey.api.APIClient({ origin: `https://${host}`, credential: signinRes.i }), + { username, password }, + ]; +} + +function parseAcct(acct: string): { username: string; host: string | null } { + const split = (acct.startsWith('@') ? acct.substring(1) : acct).split('@', 2); + return { username: split[0], host: split[1] ?? null }; +} + +export async function resolveRemoteAccount(from_acct: string, to_acct: string, fromClient?: Misskey.api.APIClient): Promise { + const [from, to] = [parseAcct(from_acct), parseAcct(to_acct)]; + const fromAdminClient: Misskey.api.APIClient = fromClient ?? (await fetchAdmin(from.username))[1]; + + return new Promise((resolve, reject) => { + console.log(`Resolving @${to.username}@${to.host} from @${from.username}@${from.host} ...`); + fromAdminClient.request('ap/show', { uri: `https://${to.host}/@${to.username}` }) + .then(res => { + console.log(`Resolved @${to.username}@${to.host} from @${from.username}@${from.host}`); + strictEqual(res.type, 'User'); + strictEqual(res.object.url, `https://${to.host}/@${to.username}`); + resolve(res); + }) + .catch(err => reject(err)); + }); +} + +export async function uploadFile(host: string, path: string, token: string): Promise { + const filename = path.split('/').pop() ?? 'untitled'; + const blob = new Blob([await readFile(join(__dirname, path))]); + + const body = new FormData(); + body.append('i', token); + body.append('force', 'true'); + body.append('file', blob); + body.append('name', filename); + + return new Promise((resolve, reject) => { + fetch(`https://${host}/api/drive/files/create`, { + method: 'POST', + body, + }).then(async res => { + resolve(await res.json()); + }).catch(err => { + reject(err); + }); + }); +} diff --git a/packages/backend/test-federation/tsconfig.json b/packages/backend/test-federation/tsconfig.json new file mode 100644 index 000000000000..3a1cb3b9f3ba --- /dev/null +++ b/packages/backend/test-federation/tsconfig.json @@ -0,0 +1,114 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "NodeNext", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./built", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": [ + "daemon.ts", + "./test/**/*.ts" + ] +} From 1f5feb8a90e6f38586bff35b39c5f42138c3b1c9 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 19 Sep 2024 22:59:24 +0900 Subject: [PATCH 002/111] fix(ci): install pnpm --- .github/workflows/test-federation.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml index 9ca28fe328df..4c1d114ab711 100644 --- a/.github/workflows/test-federation.yml +++ b/.github/workflows/test-federation.yml @@ -23,6 +23,8 @@ jobs: node-version: [20.16.0] steps: - uses: actions/checkout@v4 + - name: Install pnpm + uses: pnpm/action-setup@v4 - name: Install FFmpeg uses: FedericoCarboni/setup-ffmpeg@v3 - name: Use Node.js ${{ matrix.node-version }} From 0d89ea07ae2a0c05c4be8b87507a32cbed9fddae Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:01:42 +0900 Subject: [PATCH 003/111] fix(ci): cd --- .github/workflows/test-federation.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml index 4c1d114ab711..b507de674e0e 100644 --- a/.github/workflows/test-federation.yml +++ b/.github/workflows/test-federation.yml @@ -50,8 +50,14 @@ jobs: sudo chmod 644 ./certificates/*.local.key - name: Start servers # https://github.com/docker/compose/issues/1294#issuecomment-374847206 - run: docker compose up -d --scale tester=0 + run: | + cd packages/backend/test-federation + docker compose up -d --scale tester=0 - name: Test - run: docker compose run tester + run: | + cd packages/backend/test-federation + docker compose run tester - name: Stop servers - run: docker compose down + run: | + cd packages/backend/test-federation + docker compose down From 7a55d9b02d9c323cccbf803516e792b7fdea5cf8 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:04:24 +0900 Subject: [PATCH 004/111] fix(ci): build entire project --- .github/workflows/test-federation.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml index b507de674e0e..d543b7432d94 100644 --- a/.github/workflows/test-federation.yml +++ b/.github/workflows/test-federation.yml @@ -35,12 +35,8 @@ jobs: - name: Build Misskey run: | corepack enable && corepack prepare - pnpm -F misskey-js i --frozen-lockfile - pnpm -F misskey-js build - pnpm -F misskey-reversi i --frozen-lockfile - pnpm -F misskey-reversi build - pnpm -F backend i --frozen-lockfile - pnpm -F backend build + pnpm i --frozen-lockfile + pnpm build pnpm -F backend build:fed - name: Setup run: | From 2bdf1a52734582b0bc0de1b990da228b35373774 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:08:31 +0900 Subject: [PATCH 005/111] fix(ci): skip frontend build --- .github/workflows/test-federation.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml index d543b7432d94..c71a2e9c9907 100644 --- a/.github/workflows/test-federation.yml +++ b/.github/workflows/test-federation.yml @@ -35,8 +35,8 @@ jobs: - name: Build Misskey run: | corepack enable && corepack prepare - pnpm i --frozen-lockfile - pnpm build + pnpm --filter=!frontend i --frozen-lockfile + pnpm --filter=!frontend build pnpm -F backend build:fed - name: Setup run: | From 19aed9611f4c527d57a6ce4d8c634525df45ceb4 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:12:08 +0900 Subject: [PATCH 006/111] fix(ci): pull submodule when checkout --- .github/workflows/test-federation.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml index c71a2e9c9907..6bba02d007c8 100644 --- a/.github/workflows/test-federation.yml +++ b/.github/workflows/test-federation.yml @@ -23,6 +23,8 @@ jobs: node-version: [20.16.0] steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Install pnpm uses: pnpm/action-setup@v4 - name: Install FFmpeg From a930964b8d6ba550c23bce1e7fb45d92eab49ef9 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:19:24 +0900 Subject: [PATCH 007/111] chore: show log for debugging --- .github/workflows/test-federation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml index 6bba02d007c8..9b12c01f3ac1 100644 --- a/.github/workflows/test-federation.yml +++ b/.github/workflows/test-federation.yml @@ -50,7 +50,7 @@ jobs: # https://github.com/docker/compose/issues/1294#issuecomment-374847206 run: | cd packages/backend/test-federation - docker compose up -d --scale tester=0 + docker compose up --abort-on-container-exit --scale tester=0 - name: Test run: | cd packages/backend/test-federation From eff9cd96a973523341053b1d87719fe7333ec292 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:23:45 +0900 Subject: [PATCH 008/111] Revert "chore: show log for debugging" This reverts commit a930964b8d6ba550c23bce1e7fb45d92eab49ef9. --- .github/workflows/test-federation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml index 9b12c01f3ac1..6bba02d007c8 100644 --- a/.github/workflows/test-federation.yml +++ b/.github/workflows/test-federation.yml @@ -50,7 +50,7 @@ jobs: # https://github.com/docker/compose/issues/1294#issuecomment-374847206 run: | cd packages/backend/test-federation - docker compose up --abort-on-container-exit --scale tester=0 + docker compose up -d --scale tester=0 - name: Test run: | cd packages/backend/test-federation From f3759b32a877d09f934711486492ccc78b6fea69 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:24:32 +0900 Subject: [PATCH 009/111] fix(ci): build entire project --- .github/workflows/test-federation.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml index 6bba02d007c8..a95f6f2d74da 100644 --- a/.github/workflows/test-federation.yml +++ b/.github/workflows/test-federation.yml @@ -37,8 +37,8 @@ jobs: - name: Build Misskey run: | corepack enable && corepack prepare - pnpm --filter=!frontend i --frozen-lockfile - pnpm --filter=!frontend build + pnpm i --frozen-lockfile + pnpm build pnpm -F backend build:fed - name: Setup run: | From 7cfb75d47fc38c9e087a2533be5518c9da6cb86d Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:34:26 +0900 Subject: [PATCH 010/111] chore: omit unused globals --- packages/backend/test-federation/eslint.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/test-federation/eslint.config.js b/packages/backend/test-federation/eslint.config.js index a0f43babadfb..e3bcf4c0fe22 100644 --- a/packages/backend/test-federation/eslint.config.js +++ b/packages/backend/test-federation/eslint.config.js @@ -9,7 +9,6 @@ export default [ languageOptions: { globals: { ...globals.node, - ...globals.jest, }, parserOptions: { parser: tsParser, From 28af0f3589cc281eb7b28fb800df8d657bb9371b Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:39:23 +0900 Subject: [PATCH 011/111] refactor: use strictEqual and simplify some asserts --- .../test-federation/test/drive.test.ts | 40 ++++++++----------- .../backend/test-federation/test/user.test.ts | 21 +++++----- 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index 892d75488b7d..b3144c8b4b1e 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -1,4 +1,4 @@ -import { deepEqual, deepStrictEqual, strictEqual } from 'node:assert'; +import { deepStrictEqual, strictEqual } from 'node:assert'; import test, { describe } from 'node:test'; import * as Misskey from 'misskey-js'; import { createAccount, fetchAdmin, uploadFile } from './utils.js'; @@ -18,14 +18,11 @@ describe('Drive', () => { const image = await uploadFile('a.local', '../../test/resources/192.jpg', uploader.i); const noteWithImage = (await uploaderClient.request('notes/create', { fileIds: [image.id] })).createdNote; const uri = `https://a.local/notes/${noteWithImage.id}`; - const noteInBServer = await (async (): Promise => { - const resolved = await bAdminClient.request('ap/show', { uri }); - strictEqual(resolved.type, 'Note'); - return resolved; - })(); - deepEqual(noteInBServer.object.uri, uri); - deepEqual(noteInBServer.object.files != null, true); - deepEqual(noteInBServer.object.files!.length, 1); + const noteInBServer = await bAdminClient.request('ap/show', { uri }); + strictEqual(noteInBServer.type, 'Note'); + strictEqual(noteInBServer.object.uri, uri); + strictEqual(noteInBServer.object.files != null, true); + strictEqual(noteInBServer.object.files!.length, 1); const imageInBServer = noteInBServer.object.files![0]; await test('Check consistency of DriveFile', () => { @@ -67,31 +64,28 @@ describe('Drive', () => { // console.log(`b.local: ${JSON.stringify(updatedImageInBServer, null, '\t')}`); // FIXME: not updated with `drive/files/update` - deepEqual(updatedImage.isSensitive, true); - deepEqual(updatedImage.name, 'updated_192.jpg'); - deepEqual(updatedImageInBServer.isSensitive, false); - deepEqual(updatedImageInBServer.name, '192.jpg'); + strictEqual(updatedImage.isSensitive, true); + strictEqual(updatedImage.name, 'updated_192.jpg'); + strictEqual(updatedImageInBServer.isSensitive, false); + strictEqual(updatedImageInBServer.name, '192.jpg'); }); const noteWithUpdatedImage = (await uploaderClient.request('notes/create', { fileIds: [updatedImage.id] })).createdNote; const uriUpdated = `https://a.local/notes/${noteWithUpdatedImage.id}`; - const noteWithUpdatedImageInBServer = await (async (): Promise => { - const resolved = await bAdminClient.request('ap/show', { uri: uriUpdated }); - strictEqual(resolved.type, 'Note'); - return resolved; - })(); - deepEqual(noteWithUpdatedImageInBServer.object.uri, uriUpdated); - deepEqual(noteWithUpdatedImageInBServer.object.files != null, true); - deepEqual(noteWithUpdatedImageInBServer.object.files!.length, 1); + const noteWithUpdatedImageInBServer = await bAdminClient.request('ap/show', { uri: uriUpdated }); + strictEqual(noteWithUpdatedImageInBServer.type, 'Note'); + strictEqual(noteWithUpdatedImageInBServer.object.uri, uriUpdated); + strictEqual(noteWithUpdatedImageInBServer.object.files != null, true); + strictEqual(noteWithUpdatedImageInBServer.object.files!.length, 1); const reupdatedImageInBServer = noteWithUpdatedImageInBServer.object.files![0]; await test('Re-update with attaching to Note', async () => { // console.log(`b.local: ${JSON.stringify(reupdatedImageInBServer, null, '\t')}`); // `isSensitive` is updated - deepEqual(reupdatedImageInBServer.isSensitive, true); + strictEqual(reupdatedImageInBServer.isSensitive, true); // FIXME: but `name` is not updated - deepEqual(reupdatedImageInBServer.name, '192.jpg'); + strictEqual(reupdatedImageInBServer.name, '192.jpg'); }); }); }); diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index 1e691d18522c..d49c59ce09fd 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -1,4 +1,4 @@ -import { deepEqual, deepStrictEqual, strictEqual } from 'node:assert'; +import { deepStrictEqual, strictEqual } from 'node:assert'; import test, { before, describe } from 'node:test'; import * as Misskey from 'misskey-js'; import { createAccount, fetchAdmin, resolveRemoteAccount } from './utils.js'; @@ -25,13 +25,10 @@ describe('User', () => { const aliceInAServer = await aliceWatcherClient.request('users/show', { userId: alice.id }); - const resolved = await (async (): Promise => { - const resolved = await aliceWatcherInBServerClient.request('ap/show', { - uri: `https://a.local/@${aliceInAServer.username}`, - }); - strictEqual(resolved.type, 'User'); - return resolved; - })(); + const resolved = await aliceWatcherInBServerClient.request('ap/show', { + uri: `https://a.local/@${aliceInAServer.username}`, + }); + strictEqual(resolved.type, 'User'); const aliceInBServer = await aliceWatcherInBServerClient.request('users/show', { userId: resolved.object.id }); @@ -86,12 +83,12 @@ describe('User', () => { test('Check consistency with `users/following` and `users/followers` endpoints', async () => { await Promise.all([ - deepEqual( + strictEqual( (await aliceClient.request('users/following', { userId: alice.id })) .some(v => v.followeeId === bobInAServer.object.id), true, ), - deepEqual( + strictEqual( (await bobClient.request('users/followers', { userId: bob.id })) .some(v => v.followerId === aliceInBServer.object.id), true, @@ -112,12 +109,12 @@ describe('User', () => { test('Check consistency with `users/following` and `users/followers` endpoints', async () => { await Promise.all([ - deepEqual( + strictEqual( (await aliceClient.request('users/following', { userId: alice.id })) .some(v => v.followeeId === bobInAServer.object.id), false, ), - deepEqual( + strictEqual( (await bobClient.request('users/followers', { userId: bob.id })) .some(v => v.followerId === aliceInBServer.object.id), false, From df691907dfd1ee426893f5fa510853b0200526d7 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:43:51 +0900 Subject: [PATCH 012/111] test: follow requests --- .../backend/test-federation/test/user.test.ts | 87 +++++++++++++++++-- .../backend/test-federation/test/utils.ts | 6 +- 2 files changed, 83 insertions(+), 10 deletions(-) diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index d49c59ce09fd..e700580dd939 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -1,4 +1,4 @@ -import { deepStrictEqual, strictEqual } from 'node:assert'; +import { deepStrictEqual, rejects, strictEqual } from 'node:assert'; import test, { before, describe } from 'node:test'; import * as Misskey from 'misskey-js'; import { createAccount, fetchAdmin, resolveRemoteAccount } from './utils.js'; @@ -74,7 +74,7 @@ describe('User', () => { await describe('Follow a.local ==> b.local', async () => { before(async () => { console.log(`Following ${bobAcct} from ${aliceAcct} ...`); - await aliceClient.request('following/create', { userId: bobInAServer.object.id }); + await aliceClient.request('following/create', { userId: bobInAServer.id }); console.log(`Followed ${bobAcct} from ${aliceAcct}`); // wait for 1 secound @@ -85,12 +85,12 @@ describe('User', () => { await Promise.all([ strictEqual( (await aliceClient.request('users/following', { userId: alice.id })) - .some(v => v.followeeId === bobInAServer.object.id), + .some(v => v.followeeId === bobInAServer.id), true, ), strictEqual( (await bobClient.request('users/followers', { userId: bob.id })) - .some(v => v.followerId === aliceInBServer.object.id), + .some(v => v.followerId === aliceInBServer.id), true, ), ]); @@ -100,7 +100,7 @@ describe('User', () => { await describe('Unfollow a.local ==> b.local', async () => { before(async () => { console.log(`Unfollowing ${bobAcct} from ${aliceAcct} ...`); - await aliceClient.request('following/delete', { userId: bobInAServer.object.id }); + await aliceClient.request('following/delete', { userId: bobInAServer.id }); console.log(`Unfollowed ${bobAcct} from ${aliceAcct}`); // wait for 1 secound @@ -111,16 +111,89 @@ describe('User', () => { await Promise.all([ strictEqual( (await aliceClient.request('users/following', { userId: alice.id })) - .some(v => v.followeeId === bobInAServer.object.id), + .some(v => v.followeeId === bobInAServer.id), false, ), strictEqual( (await bobClient.request('users/followers', { userId: bob.id })) - .some(v => v.followerId === aliceInBServer.object.id), + .some(v => v.followerId === aliceInBServer.id), false, ), ]); }); }); }); + + describe('Follow requests', async () => { + const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); + const [bob, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + + const aliceAcct = `@${aliceUsername}@a.local`; + const bobAcct = `@${bobUsername}@b.local`; + + const [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteAccount(aliceAcct, bobAcct, aliceClient), + resolveRemoteAccount(bobAcct, aliceAcct, bobClient), + ]); + + await aliceClient.request('i/update', { isLocked: true }); + + await test('Send follow request from Bob to Alice and cancel', async () => { + await bobClient.request('following/create', { userId: aliceInBServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + await test('Alice should have a request', async () => { + const requests = await aliceClient.request('following/requests/list', {}); + strictEqual(requests.length, 1); + strictEqual(requests[0].followee.id, alice.id); + strictEqual(requests[0].follower.id, bobInAServer.id); + }); + + await bobClient.request('following/requests/cancel', { userId: aliceInBServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + await test('Alice should have no requests', async () => { + const requests = await aliceClient.request('following/requests/list', {}); + strictEqual(requests.length, 0); + }); + }); + + await test('Send follow request from Bob to Alice and reject', async () => { + await bobClient.request('following/create', { userId: aliceInBServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + await aliceClient.request('following/requests/reject', { userId: bobInAServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + await test('Bob should have no requests', async () => { + await rejects( + async () => await bobClient.request('following/requests/cancel', { userId: aliceInBServer.id }), + (err: any) => { + strictEqual(err.code, 'FOLLOW_REQUEST_NOT_FOUND'); + return true; + }, + ); + }); + + await test('Bob doesn\'t follow Alice', async () => { + const following = await bobClient.request('users/following', { userId: bob.id }); + strictEqual(following.length, 0); + }); + }); + + await test('Send follow request from Bob to Alice and accept', async () => { + await bobClient.request('following/create', { userId: aliceInBServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + await aliceClient.request('following/requests/accept', { userId: bobInAServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + await test('Bob follows Alice', async () => { + const following = await bobClient.request('users/following', { userId: bob.id }); + strictEqual(following.length, 1); + strictEqual(following[0].followeeId, aliceInBServer.id); + strictEqual(following[0].followerId, bob.id); + }); + }); + }); }); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 77a915ae35c5..9a5d86e84945 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -97,18 +97,18 @@ function parseAcct(acct: string): { username: string; host: string | null } { return { username: split[0], host: split[1] ?? null }; } -export async function resolveRemoteAccount(from_acct: string, to_acct: string, fromClient?: Misskey.api.APIClient): Promise { +export async function resolveRemoteAccount(from_acct: string, to_acct: string, fromClient?: Misskey.api.APIClient): Promise { const [from, to] = [parseAcct(from_acct), parseAcct(to_acct)]; const fromAdminClient: Misskey.api.APIClient = fromClient ?? (await fetchAdmin(from.username))[1]; - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { console.log(`Resolving @${to.username}@${to.host} from @${from.username}@${from.host} ...`); fromAdminClient.request('ap/show', { uri: `https://${to.host}/@${to.username}` }) .then(res => { console.log(`Resolved @${to.username}@${to.host} from @${from.username}@${from.host}`); strictEqual(res.type, 'User'); strictEqual(res.object.url, `https://${to.host}/@${to.username}`); - resolve(res); + resolve(res.object); }) .catch(err => reject(err)); }); From 2cee61d29e30964998c976716bb17b962fd08204 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:50:11 +0900 Subject: [PATCH 013/111] refactor: add resolveRemoteNote function --- .../test-federation/test/drive.test.ts | 22 ++++++++----------- .../backend/test-federation/test/utils.ts | 12 ++++++++++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index b3144c8b4b1e..6a1c2a8fdad7 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -1,7 +1,7 @@ import { deepStrictEqual, strictEqual } from 'node:assert'; import test, { describe } from 'node:test'; import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, uploadFile } from './utils.js'; +import { createAccount, fetchAdmin, resolveRemoteNote, uploadFile } from './utils.js'; const [ [, aAdminClient], @@ -18,12 +18,10 @@ describe('Drive', () => { const image = await uploadFile('a.local', '../../test/resources/192.jpg', uploader.i); const noteWithImage = (await uploaderClient.request('notes/create', { fileIds: [image.id] })).createdNote; const uri = `https://a.local/notes/${noteWithImage.id}`; - const noteInBServer = await bAdminClient.request('ap/show', { uri }); - strictEqual(noteInBServer.type, 'Note'); - strictEqual(noteInBServer.object.uri, uri); - strictEqual(noteInBServer.object.files != null, true); - strictEqual(noteInBServer.object.files!.length, 1); - const imageInBServer = noteInBServer.object.files![0]; + const noteInBServer = await resolveRemoteNote(uri, bAdminClient); + strictEqual(noteInBServer.files != null, true); + strictEqual(noteInBServer.files!.length, 1); + const imageInBServer = noteInBServer.files![0]; await test('Check consistency of DriveFile', () => { // console.log(`a.local: ${JSON.stringify(image, null, '\t')}`); @@ -72,12 +70,10 @@ describe('Drive', () => { const noteWithUpdatedImage = (await uploaderClient.request('notes/create', { fileIds: [updatedImage.id] })).createdNote; const uriUpdated = `https://a.local/notes/${noteWithUpdatedImage.id}`; - const noteWithUpdatedImageInBServer = await bAdminClient.request('ap/show', { uri: uriUpdated }); - strictEqual(noteWithUpdatedImageInBServer.type, 'Note'); - strictEqual(noteWithUpdatedImageInBServer.object.uri, uriUpdated); - strictEqual(noteWithUpdatedImageInBServer.object.files != null, true); - strictEqual(noteWithUpdatedImageInBServer.object.files!.length, 1); - const reupdatedImageInBServer = noteWithUpdatedImageInBServer.object.files![0]; + const noteWithUpdatedImageInBServer = await resolveRemoteNote(uriUpdated, bAdminClient); + strictEqual(noteWithUpdatedImageInBServer.files != null, true); + strictEqual(noteWithUpdatedImageInBServer.files!.length, 1); + const reupdatedImageInBServer = noteWithUpdatedImageInBServer.files![0]; await test('Re-update with attaching to Note', async () => { // console.log(`b.local: ${JSON.stringify(reupdatedImageInBServer, null, '\t')}`); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 9a5d86e84945..a5cb32ead49f 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -114,6 +114,18 @@ export async function resolveRemoteAccount(from_acct: string, to_acct: string, f }); } +export async function resolveRemoteNote(uri: string, fromClient: Misskey.api.APIClient): Promise { + return new Promise((resolve, reject) => { + fromClient.request('ap/show', { uri }) + .then(res => { + strictEqual(res.type, 'Note'); + strictEqual(res.object.uri, uri); + resolve(res.object); + }) + .catch(err => reject(err)); + }); +} + export async function uploadFile(host: string, path: string, token: string): Promise { const filename = path.split('/').pop() ?? 'untitled'; const blob = new Blob([await readFile(join(__dirname, path))]); From d80205e088affc382b1dcf33b5f0acda529ec7e2 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:56:09 +0900 Subject: [PATCH 014/111] refactor: refine resolveRemoteUser function --- .../backend/test-federation/test/user.test.ts | 27 +++++-------------- .../backend/test-federation/test/utils.ts | 16 +++-------- 2 files changed, 10 insertions(+), 33 deletions(-) diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index e700580dd939..a2b0de6df690 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -1,7 +1,7 @@ import { deepStrictEqual, rejects, strictEqual } from 'node:assert'; import test, { before, describe } from 'node:test'; import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, resolveRemoteAccount } from './utils.js'; +import { createAccount, fetchAdmin, resolveRemoteUser } from './utils.js'; const [ [, aAdminClient], @@ -25,12 +25,9 @@ describe('User', () => { const aliceInAServer = await aliceWatcherClient.request('users/show', { userId: alice.id }); - const resolved = await aliceWatcherInBServerClient.request('ap/show', { - uri: `https://a.local/@${aliceInAServer.username}`, - }); - strictEqual(resolved.type, 'User'); + const resolved = await resolveRemoteUser(`https://a.local/@${aliceInAServer.username}`, aliceWatcherInBServerClient); - const aliceInBServer = await aliceWatcherInBServerClient.request('users/show', { userId: resolved.object.id }); + const aliceInBServer = await aliceWatcherInBServerClient.request('users/show', { userId: resolved.id }); // console.log(`a.local: ${JSON.stringify(aliceInAServer, null, '\t')}`); // console.log(`b.local: ${JSON.stringify(aliceInBServer, null, '\t')}`); @@ -63,19 +60,14 @@ describe('User', () => { const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); const [bob, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); - const aliceAcct = `@${aliceUsername}@a.local`; - const bobAcct = `@${bobUsername}@b.local`; - const [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteAccount(aliceAcct, bobAcct, aliceClient), - resolveRemoteAccount(bobAcct, aliceAcct, bobClient), + resolveRemoteUser(`https://b.local/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.local/@${aliceUsername}`, bobClient), ]); await describe('Follow a.local ==> b.local', async () => { before(async () => { - console.log(`Following ${bobAcct} from ${aliceAcct} ...`); await aliceClient.request('following/create', { userId: bobInAServer.id }); - console.log(`Followed ${bobAcct} from ${aliceAcct}`); // wait for 1 secound await new Promise(resolve => setTimeout(resolve, 1000)); @@ -99,9 +91,7 @@ describe('User', () => { await describe('Unfollow a.local ==> b.local', async () => { before(async () => { - console.log(`Unfollowing ${bobAcct} from ${aliceAcct} ...`); await aliceClient.request('following/delete', { userId: bobInAServer.id }); - console.log(`Unfollowed ${bobAcct} from ${aliceAcct}`); // wait for 1 secound await new Promise(resolve => setTimeout(resolve, 1000)); @@ -128,12 +118,9 @@ describe('User', () => { const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); const [bob, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); - const aliceAcct = `@${aliceUsername}@a.local`; - const bobAcct = `@${bobUsername}@b.local`; - const [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteAccount(aliceAcct, bobAcct, aliceClient), - resolveRemoteAccount(bobAcct, aliceAcct, bobClient), + resolveRemoteUser(`https://b.local/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.local/@${aliceUsername}`, bobClient), ]); await aliceClient.request('i/update', { isLocked: true }); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index a5cb32ead49f..5c3ac686dabc 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -92,22 +92,12 @@ export async function createAccount(host: string, adminClient: Misskey.api.APICl ]; } -function parseAcct(acct: string): { username: string; host: string | null } { - const split = (acct.startsWith('@') ? acct.substring(1) : acct).split('@', 2); - return { username: split[0], host: split[1] ?? null }; -} - -export async function resolveRemoteAccount(from_acct: string, to_acct: string, fromClient?: Misskey.api.APIClient): Promise { - const [from, to] = [parseAcct(from_acct), parseAcct(to_acct)]; - const fromAdminClient: Misskey.api.APIClient = fromClient ?? (await fetchAdmin(from.username))[1]; - +export async function resolveRemoteUser(url: string, fromClient: Misskey.api.APIClient): Promise { return new Promise((resolve, reject) => { - console.log(`Resolving @${to.username}@${to.host} from @${from.username}@${from.host} ...`); - fromAdminClient.request('ap/show', { uri: `https://${to.host}/@${to.username}` }) + fromClient.request('ap/show', { uri: url }) .then(res => { - console.log(`Resolved @${to.username}@${to.host} from @${from.username}@${from.host}`); strictEqual(res.type, 'User'); - strictEqual(res.object.url, `https://${to.host}/@${to.username}`); + strictEqual(res.object.url, url); resolve(res.object); }) .catch(err => reject(err)); From fdcd9290356d7c56e0c31cf745b5ec076fdb47f8 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:10:04 +0900 Subject: [PATCH 015/111] refactor: cache admin credentials --- packages/backend/test-federation/test/utils.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 5c3ac686dabc..15ddebde2ea5 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -42,10 +42,17 @@ export async function signin(host: string, params: Misskey.entities.SigninReques }); } +const adminCache = new Map(); + async function createAdmin(host: string): Promise { const client = new Misskey.api.APIClient({ origin: `https://${host}` }); return await client.request('admin/accounts/create', ADMIN_PARAMS).then(res => { console.log(`Successfully created admin account: @${ADMIN_PARAMS.username}@${host}`); + adminCache.set(host, { + id: res.id, + // @ts-expect-error FIXME: openapi-typescript generates incorrect response type for this endpoint, so ignore this + i: res.token, + }); return res as Misskey.entities.SignupResponse; }).then(async res => { await client.request('admin/roles/update-default-policies', { @@ -64,7 +71,13 @@ async function createAdmin(host: string): Promise { - const admin = await signin(host, ADMIN_PARAMS) + await new Promise(resolve => setTimeout(resolve, Math.random() * 5000)); + + const admin = adminCache.get(host) ?? await signin(host, ADMIN_PARAMS) + .then(res => { + adminCache.set(host, res); + return res; + }) .catch(async err => { if (err.id === '6cc579cc-885d-43d8-95c2-b8c7fc963280') { await createAdmin(host); From ab41c4daed64a3ca3fa2e733ee7e4a8415d0c60b Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:29:53 +0900 Subject: [PATCH 016/111] refactor: simplify assertion with excluded fields --- .../test-federation/test/drive.test.ts | 19 ++++--------------- .../backend/test-federation/test/user.test.ts | 18 ++++-------------- .../backend/test-federation/test/utils.ts | 13 ++++++++++++- 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index 6a1c2a8fdad7..b48795183d93 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -1,7 +1,6 @@ -import { deepStrictEqual, strictEqual } from 'node:assert'; +import { strictEqual } from 'node:assert'; import test, { describe } from 'node:test'; -import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, resolveRemoteNote, uploadFile } from './utils.js'; +import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, uploadFile } from './utils.js'; const [ [, aAdminClient], @@ -27,24 +26,14 @@ describe('Drive', () => { // console.log(`a.local: ${JSON.stringify(image, null, '\t')}`); // console.log(`b.local: ${JSON.stringify(imageInBServer, null, '\t')}`); - const toBeDeleted: (keyof Misskey.entities.DriveFile)[] = [ + deepStrictEqualWithExcludedFields(image, imageInBServer, [ 'id', 'createdAt', 'size', 'url', 'thumbnailUrl', 'userId', - ]; - const _Image: Partial = structuredClone(image); - const _ImageInBServer: Partial = structuredClone(imageInBServer); - - for (const image of [_Image, _ImageInBServer]) { - for (const field of toBeDeleted) { - delete image[field]; - } - } - - deepStrictEqual(_Image, _ImageInBServer); + ]); }); const updatedImage = await uploaderClient.request('drive/files/update', { diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index a2b0de6df690..a10bbaa2ad0c 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -1,7 +1,6 @@ -import { deepStrictEqual, rejects, strictEqual } from 'node:assert'; +import { rejects, strictEqual } from 'node:assert'; import test, { before, describe } from 'node:test'; -import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, resolveRemoteUser } from './utils.js'; +import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteUser } from './utils.js'; const [ [, aAdminClient], @@ -32,7 +31,7 @@ describe('User', () => { // console.log(`a.local: ${JSON.stringify(aliceInAServer, null, '\t')}`); // console.log(`b.local: ${JSON.stringify(aliceInBServer, null, '\t')}`); - const toBeDeleted: (keyof Misskey.entities.UserDetailedNotMe)[] = [ + deepStrictEqualWithExcludedFields(aliceInAServer, aliceInBServer, [ 'id', 'host', 'avatarUrl', @@ -43,16 +42,7 @@ describe('User', () => { 'createdAt', 'lastFetchedAt', 'publicReactions', - ]; - const _aliceInAServer: Partial = structuredClone(aliceInAServer); - const _aliceInBServer: Partial = structuredClone(aliceInBServer); - for (const alice of [_aliceInAServer, _aliceInBServer]) { - for (const field of toBeDeleted) { - delete alice[field]; - } - } - - deepStrictEqual(_aliceInAServer, _aliceInBServer); + ]); }); }); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 15ddebde2ea5..436de27fdcf5 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -1,4 +1,4 @@ -import { strictEqual } from 'assert'; +import { deepStrictEqual, strictEqual } from 'assert'; import { readFile } from 'fs/promises'; import { dirname, join } from 'path'; import { fileURLToPath } from 'url'; @@ -150,3 +150,14 @@ export async function uploadFile(host: string, path: string, token: string): Pro }); }); } + +export function deepStrictEqualWithExcludedFields(actual: T, expected: T, excludedFields: (keyof T)[]) { + const _actual = structuredClone(actual); + const _expected = structuredClone(expected); + for (const obj of [_actual, _expected]) { + for (const field of excludedFields) { + delete obj[field]; + } + } + deepStrictEqual(_actual, _expected); +} From 3b38df643c433809a8ec9235b419a3752f73e005 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Fri, 20 Sep 2024 17:02:49 +0900 Subject: [PATCH 017/111] refactor: use assert --- .../backend/test-federation/test/drive.test.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index b48795183d93..d204edd88a17 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -1,4 +1,4 @@ -import { strictEqual } from 'node:assert'; +import assert, { strictEqual } from 'node:assert'; import test, { describe } from 'node:test'; import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, uploadFile } from './utils.js'; @@ -18,9 +18,9 @@ describe('Drive', () => { const noteWithImage = (await uploaderClient.request('notes/create', { fileIds: [image.id] })).createdNote; const uri = `https://a.local/notes/${noteWithImage.id}`; const noteInBServer = await resolveRemoteNote(uri, bAdminClient); - strictEqual(noteInBServer.files != null, true); - strictEqual(noteInBServer.files!.length, 1); - const imageInBServer = noteInBServer.files![0]; + assert(noteInBServer.files != null); + strictEqual(noteInBServer.files.length, 1); + const imageInBServer = noteInBServer.files[0]; await test('Check consistency of DriveFile', () => { // console.log(`a.local: ${JSON.stringify(image, null, '\t')}`); @@ -60,9 +60,9 @@ describe('Drive', () => { const noteWithUpdatedImage = (await uploaderClient.request('notes/create', { fileIds: [updatedImage.id] })).createdNote; const uriUpdated = `https://a.local/notes/${noteWithUpdatedImage.id}`; const noteWithUpdatedImageInBServer = await resolveRemoteNote(uriUpdated, bAdminClient); - strictEqual(noteWithUpdatedImageInBServer.files != null, true); - strictEqual(noteWithUpdatedImageInBServer.files!.length, 1); - const reupdatedImageInBServer = noteWithUpdatedImageInBServer.files![0]; + assert(noteWithUpdatedImageInBServer.files != null); + strictEqual(noteWithUpdatedImageInBServer.files.length, 1); + const reupdatedImageInBServer = noteWithUpdatedImageInBServer.files[0]; await test('Re-update with attaching to Note', async () => { // console.log(`b.local: ${JSON.stringify(reupdatedImageInBServer, null, '\t')}`); From f8a382024596665b4ad4797accab2cf2cb46c2fc Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Fri, 20 Sep 2024 18:12:12 +0900 Subject: [PATCH 018/111] test: note --- .../backend/test-federation/test/note.test.ts | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 packages/backend/test-federation/test/note.test.ts diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts new file mode 100644 index 000000000000..114cbdc3f0a4 --- /dev/null +++ b/packages/backend/test-federation/test/note.test.ts @@ -0,0 +1,159 @@ +import test, { describe } from 'node:test'; +import assert, { rejects, strictEqual } from 'node:assert'; +import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, resolveRemoteUser, uploadFile } from './utils.js'; + +const [ + [, aAdminClient], + [, bAdminClient], +] = await Promise.all([ + fetchAdmin('a.local'), + fetchAdmin('b.local'), +]); + +describe('Note', async () => { + const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); + const [, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + + const [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser(`https://b.local/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.local/@${aliceUsername}`, bobClient), + ]); + + describe('Note content', () => { + test('Consistency of Public Note', async () => { + const image = await uploadFile('a.local', '../../test/resources/192.jpg', alice.i); + const note = (await aliceClient.request('notes/create', { + text: 'I am Alice!', + fileIds: [image.id], + poll: { + choices: ['neko', 'inu'], + multiple: false, + expiredAfter: 60 * 60 * 1000, + }, + })).createdNote; + + const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + deepStrictEqualWithExcludedFields(note, resolvedNote, [ + 'id', + 'emojis', + /** Consistency of files is checked at {@link file://./drive.test.ts}, so let's skip. */ + 'fileIds', + 'files', + 'reactionAcceptance', + 'userId', + 'user', + 'uri', + ]); + strictEqual(aliceInBServer.id, resolvedNote.userId); + }); + + test('Consistency of reply', async () => { + const _replyedNote = (await aliceClient.request('notes/create', { + text: 'a', + })).createdNote; + const note = (await aliceClient.request('notes/create', { + text: 'b', + replyId: _replyedNote.id, + })).createdNote; + // NOTE: the repliedCount is incremented, so fetch again + const replyedNote = await aliceClient.request('notes/show', { noteId: _replyedNote.id }); + strictEqual(replyedNote.repliesCount, 1); + + const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + deepStrictEqualWithExcludedFields(note, resolvedNote, [ + 'id', + 'emojis', + 'reactionAcceptance', + 'replyId', + 'reply', + 'userId', + 'user', + 'uri', + ]); + assert(resolvedNote.replyId != null); + assert(resolvedNote.reply != null); + deepStrictEqualWithExcludedFields(replyedNote, resolvedNote.reply, [ + 'id', + // TODO: why clippedCount loses consistency? + 'clippedCount', + 'emojis', + 'userId', + 'user', + 'uri', + // flaky because this is parallelly incremented, so let's check it below + 'repliesCount', + ]); + strictEqual(aliceInBServer.id, resolvedNote.userId); + + await new Promise(resolve => setTimeout(resolve, 1000)); + + const resolvedReplyedNote = await bobClient.request('notes/show', { noteId: resolvedNote.replyId }); + strictEqual(resolvedReplyedNote.repliesCount, 1); + }); + + test('Consistency of Renote', async () => { + // NOTE: the renoteCount is not incremented, so no need to fetch again + const renotedNote = (await aliceClient.request('notes/create', { + text: 'a', + })).createdNote; + const note = (await aliceClient.request('notes/create', { + text: 'b', + renoteId: renotedNote.id, + })).createdNote; + + const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + deepStrictEqualWithExcludedFields(note, resolvedNote, [ + 'id', + 'emojis', + 'reactionAcceptance', + 'renoteId', + 'renote', + 'userId', + 'user', + 'uri', + ]); + assert(resolvedNote.renoteId != null); + assert(resolvedNote.renote != null); + deepStrictEqualWithExcludedFields(renotedNote, resolvedNote.renote, [ + 'id', + 'emojis', + 'userId', + 'user', + 'uri', + ]); + strictEqual(aliceInBServer.id, resolvedNote.userId); + }); + }); + + describe('Other props', () => { + test('localOnly', async () => { + const note = (await aliceClient.request('notes/create', { text: 'a', localOnly: true })).createdNote; + rejects( + async () => await bobClient.request('ap/show', { uri: `https://a.local/notes/${note.id}` }), + (err: any) => { + /** + * FIXME: this error is not handled + * @see https://github.com/misskey-dev/misskey/issues/12736 + */ + strictEqual(err.code, 'INTERNAL_ERROR'); + return true; + }, + ); + }); + }); + + describe('Reaction', () => { + test('Consistency of reaction', async () => { + const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + const reaction = '😅'; + await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + const reactions = await aliceClient.request('notes/reactions', { noteId: note.id }); + strictEqual(reactions.length, 1); + strictEqual(reactions[0].type, reaction); + strictEqual(reactions[0].user.id, bobInAServer.id); + }); + }); +}); From 72b3fbcc4caaefc65e6b71dbc50dd2cc989897d4 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Fri, 20 Sep 2024 22:21:49 +0900 Subject: [PATCH 019/111] chore: labeler detect federation --- .github/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index a77f73706b9e..b64d726d659a 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -6,7 +6,7 @@ 'packages/backend:test': - any: - changed-files: - - any-glob-to-any-file: ['packages/backend/test/**/*'] + - any-glob-to-any-file: ['packages/backend/test/**/*', 'packages/backend/test-federation/**/*'] 'packages/frontend': - any: From 1d0464c1d820e6af3e10ffd5f05f116b76dbfcba Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Fri, 20 Sep 2024 22:41:10 +0900 Subject: [PATCH 020/111] test: blocking --- .../test-federation/test/blocking.test.ts | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 packages/backend/test-federation/test/blocking.test.ts diff --git a/packages/backend/test-federation/test/blocking.test.ts b/packages/backend/test-federation/test/blocking.test.ts new file mode 100644 index 000000000000..f5b9afc39f0c --- /dev/null +++ b/packages/backend/test-federation/test/blocking.test.ts @@ -0,0 +1,180 @@ +import test, { describe } from 'node:test'; +import { deepStrictEqual, rejects, strictEqual } from 'node:assert'; +import { createAccount, fetchAdmin, resolveRemoteNote, resolveRemoteUser } from './utils.js'; + +const [ + [, aAdminClient], + [, bAdminClient], +] = await Promise.all([ + fetchAdmin('a.local'), + fetchAdmin('b.local'), +]); + +describe('Blocking', () => { + test('Check follow', async () => { + const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); + const [bob, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + + const [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser(`https://b.local/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.local/@${aliceUsername}`, bobClient), + ]); + + await test('Cannot follow if blocked', async () => { + await aliceClient.request('blocking/create', { userId: bobInAServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + await rejects( + async () => await bobClient.request('following/create', { userId: aliceInBServer.id }), + (err: any) => { + strictEqual(err.code, 'BLOCKED'); + return true; + }, + ); + + const following = await bobClient.request('users/following', { userId: bob.id }); + strictEqual(following.length, 0); + const followers = await aliceClient.request('users/followers', { userId: alice.id }); + strictEqual(followers.length, 0); + }); + + // FIXME: this is invalid case + await test('Cannot follow even if unblocked', async () => { + // unblock here + await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + // TODO: why still being blocked? + await rejects( + async () => await bobClient.request('following/create', { userId: aliceInBServer.id }), + (err: any) => { + strictEqual(err.code, 'BLOCKED'); + return true; + }, + ); + }); + + await test.skip('Can follow if unblocked', async () => { + await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + await bobClient.request('following/create', { userId: aliceInBServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + const following = await bobClient.request('users/following', { userId: bob.id }); + strictEqual(following.length, 1); + const followers = await aliceClient.request('users/followers', { userId: alice.id }); + strictEqual(followers.length, 1); + }); + + await test.skip('Remove follower when block them', async () => { + await test('before block', async () => { + const following = await bobClient.request('users/following', { userId: bob.id }); + strictEqual(following.length, 1); + const followers = await aliceClient.request('users/followers', { userId: alice.id }); + strictEqual(followers.length, 1); + }); + + await aliceClient.request('blocking/create', { userId: bobInAServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + await test('after block', async () => { + const following = await bobClient.request('users/following', { userId: bob.id }); + strictEqual(following.length, 0); + const followers = await aliceClient.request('users/followers', { userId: alice.id }); + strictEqual(followers.length, 0); + }); + }); + }); + + test('Check reply', async () => { + const [, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); + const [, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + + const [bobInAServer] = await Promise.all([ + resolveRemoteUser(`https://b.local/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.local/@${aliceUsername}`, bobClient), + ]); + + await test('Cannot reply if blocked', async () => { + await aliceClient.request('blocking/create', { userId: bobInAServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + await rejects( + async () => await bobClient.request('notes/create', { text: 'b', replyId: resolvedNote.id }), + (err: any) => { + strictEqual(err.code, 'YOU_HAVE_BEEN_BLOCKED'); + return true; + }, + ); + }); + + await test('Can reply if unblocked', async () => { + await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + const reply = (await bobClient.request('notes/create', { text: 'b', replyId: resolvedNote.id })).createdNote; + + await resolveRemoteNote(`https://b.local/notes/${reply.id}`, aliceClient); + }); + }); + + test('Check reaction', async () => { + const [, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); + const [, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + + const [bobInAServer] = await Promise.all([ + resolveRemoteUser(`https://b.local/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.local/@${aliceUsername}`, bobClient), + ]); + + await test('Cannot reply if blocked', async () => { + await aliceClient.request('blocking/create', { userId: bobInAServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + await rejects( + async () => await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: '😅' }), + (err: any) => { + strictEqual(err.code, 'YOU_HAVE_BEEN_BLOCKED'); + return true; + }, + ); + }); + + // FIXME: this is invalid case + await test('Cannot reply even if unblocked', async () => { + // unblock here + await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + + // TODO: why still being blocked? + await rejects( + async () => await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: '😅' }), + (err: any) => { + strictEqual(err.code, 'YOU_HAVE_BEEN_BLOCKED'); + return true; + }, + ); + }); + + await test.skip('Can reply if unblocked', async () => { + await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: '😅' }); + + const _note = await aliceClient.request('notes/show', { noteId: note.id }); + deepStrictEqual(_note.reactions, { '😅': 1 }); + }); + }); +}); From 52adf9d8391d2311125e405e020da57f04be1463 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Fri, 20 Sep 2024 23:50:23 +0900 Subject: [PATCH 021/111] test: move --- .../backend/test-federation/test/move.test.ts | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 packages/backend/test-federation/test/move.test.ts diff --git a/packages/backend/test-federation/test/move.test.ts b/packages/backend/test-federation/test/move.test.ts new file mode 100644 index 000000000000..15c6fd24d4fd --- /dev/null +++ b/packages/backend/test-federation/test/move.test.ts @@ -0,0 +1,53 @@ +import test, { describe } from 'node:test'; +import assert, { strictEqual } from 'node:assert'; +import { createAccount, fetchAdmin } from './utils.js'; + +const [ + [, aAdminClient], + [, bAdminClient], +] = await Promise.all([ + fetchAdmin('a.local'), + fetchAdmin('b.local'), +]); + +describe('Move', async () => { + await test('Minimum move', async () => { + const [, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); + const [, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + + await bobClient.request('i/update', { alsoKnownAs: [`@${aliceUsername}@a.local`] }); + await aliceClient.request('i/move', { moveToAccount: `@${bobUsername}@b.local` }); + }); + + /** @see https://github.com/misskey-dev/misskey/issues/11320 */ + await test('Following relation is transferred after move', async () => { + const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); + const [bob, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + const [carol, carolClient, { username: carolUsername }] = await createAccount('a.local', aAdminClient); + + // Follow @carol@a.local ==> @alice@a.local + await carolClient.request('following/create', { userId: alice.id }); + + // Move @alice@a.local ==> @bob@b.local + await bobClient.request('i/update', { alsoKnownAs: [`@${aliceUsername}@a.local`] }); + await aliceClient.request('i/move', { moveToAccount: `@${bobUsername}@b.local` }); + await new Promise(resolve => setTimeout(resolve, 3000)); + + await test('Check from follower', async () => { + const following = await carolClient.request('users/following', { userId: carol.id }); + strictEqual(following.length, 2); + const followees = following.map(({ followee }) => followee); + assert(followees.every(followee => followee != null)); + assert(followees.some(({ id, url }) => id === alice.id && url === null)); + assert(followees.some(({ url }) => url === `https://b.local/@${bobUsername}`)); + }); + + await test('Check from followee', async () => { + const followers = await bobClient.request('users/followers', { userId: bob.id }); + strictEqual(followers.length, 1); + const follower = followers[0].follower; + assert(follower != null); + strictEqual(follower.url, `https://a.local/@${carolUsername}`); + }); + }); +}); From fcc0e2bf805bcae71b283252f11127dec9704337 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Fri, 20 Sep 2024 23:55:58 +0900 Subject: [PATCH 022/111] fix: use appropriate TLD --- .../.config/{a.local.conf => a.test.conf} | 6 +-- .../.config/{b.local.conf => b.test.conf} | 6 +-- .../test-federation/.config/default.a.yml | 6 +-- .../test-federation/.config/default.b.yml | 6 +-- .../backend/test-federation/compose.a.yml | 24 +++++------ .../backend/test-federation/compose.b.yml | 26 ++++++------ .../test-federation/compose.override.yaml | 10 ++--- packages/backend/test-federation/compose.yml | 10 ++--- packages/backend/test-federation/daemon.ts | 6 +-- .../test-federation/generate_certificates.sh | 4 +- .../test-federation/test/blocking.test.ts | 40 +++++++++---------- .../test-federation/test/drive.test.ts | 24 +++++------ .../backend/test-federation/test/move.test.ts | 30 +++++++------- .../backend/test-federation/test/note.test.ts | 24 +++++------ .../backend/test-federation/test/user.test.ts | 36 ++++++++--------- 15 files changed, 129 insertions(+), 129 deletions(-) rename packages/backend/test-federation/.config/{a.local.conf => a.test.conf} (95%) rename packages/backend/test-federation/.config/{b.local.conf => b.test.conf} (95%) diff --git a/packages/backend/test-federation/.config/a.local.conf b/packages/backend/test-federation/.config/a.test.conf similarity index 95% rename from packages/backend/test-federation/.config/a.local.conf rename to packages/backend/test-federation/.config/a.test.conf index cbb65b6e057f..7e47a670cbae 100644 --- a/packages/backend/test-federation/.config/a.local.conf +++ b/packages/backend/test-federation/.config/a.test.conf @@ -11,7 +11,7 @@ proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=cache1:16m max_size=1g in server { listen 80; listen [::]:80; - server_name a.local; + server_name a.test; # For SSL domain validation root /var/www/html; @@ -24,7 +24,7 @@ server { listen 443 ssl; listen [::]:443 ssl; http2 on; - server_name a.local; + server_name a.test; ssl_session_timeout 1d; ssl_session_cache shared:ssl_session_cache:10m; @@ -46,7 +46,7 @@ server { # Proxy to Node location / { - proxy_pass http://misskey.a.local:3000; + proxy_pass http://misskey.a.test:3000; proxy_set_header Host $host; proxy_http_version 1.1; proxy_redirect off; diff --git a/packages/backend/test-federation/.config/b.local.conf b/packages/backend/test-federation/.config/b.test.conf similarity index 95% rename from packages/backend/test-federation/.config/b.local.conf rename to packages/backend/test-federation/.config/b.test.conf index 380b2ceef328..2d629a42ddb0 100644 --- a/packages/backend/test-federation/.config/b.local.conf +++ b/packages/backend/test-federation/.config/b.test.conf @@ -11,7 +11,7 @@ proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=cache1:16m max_size=1g in server { listen 80; listen [::]:80; - server_name b.local; + server_name b.test; # For SSL domain validation root /var/www/html; @@ -24,7 +24,7 @@ server { listen 443 ssl; listen [::]:443 ssl; http2 on; - server_name b.local; + server_name b.test; ssl_session_timeout 1d; ssl_session_cache shared:ssl_session_cache:10m; @@ -46,7 +46,7 @@ server { # Proxy to Node location / { - proxy_pass http://misskey.b.local:3000; + proxy_pass http://misskey.b.test:3000; proxy_set_header Host $host; proxy_http_version 1.1; proxy_redirect off; diff --git a/packages/backend/test-federation/.config/default.a.yml b/packages/backend/test-federation/.config/default.a.yml index 9a1456afdefd..f178bb2dbb09 100644 --- a/packages/backend/test-federation/.config/default.a.yml +++ b/packages/backend/test-federation/.config/default.a.yml @@ -1,14 +1,14 @@ -url: https://a.local/ +url: https://a.test/ port: 3000 db: - host: db.a.local + host: db.a.test port: 5432 db: misskey user: postgres pass: postgres dbReplications: false redis: - host: redis.local + host: redis.test port: 6379 id: 'aidx' proxyBypassHosts: diff --git a/packages/backend/test-federation/.config/default.b.yml b/packages/backend/test-federation/.config/default.b.yml index 0785cd7391d8..d0ee21695bf2 100644 --- a/packages/backend/test-federation/.config/default.b.yml +++ b/packages/backend/test-federation/.config/default.b.yml @@ -1,14 +1,14 @@ -url: https://b.local/ +url: https://b.test/ port: 3000 db: - host: db.b.local + host: db.b.test port: 5432 db: misskey user: postgres pass: postgres dbReplications: false redis: - host: redis.local + host: redis.test port: 6379 id: 'aidx' proxyBypassHosts: diff --git a/packages/backend/test-federation/compose.a.yml b/packages/backend/test-federation/compose.a.yml index 3046c166dfb8..0c2ce2e29492 100644 --- a/packages/backend/test-federation/compose.a.yml +++ b/packages/backend/test-federation/compose.a.yml @@ -1,23 +1,23 @@ services: - a.local: + a.test: image: nginx:1.27 depends_on: - misskey.a.local: + misskey.a.test: condition: service_healthy networks: - internal_network_a volumes: - type: bind - source: ./.config/a.local.conf - target: /etc/nginx/conf.d/a.local.conf + source: ./.config/a.test.conf + target: /etc/nginx/conf.d/a.test.conf read_only: true - type: bind - source: ./certificates/a.local.crt - target: /etc/nginx/certificates/a.local.crt + source: ./certificates/a.test.crt + target: /etc/nginx/certificates/a.test.crt read_only: true - type: bind - source: ./certificates/a.local.key - target: /etc/nginx/certificates/a.local.key + source: ./certificates/a.test.key + target: /etc/nginx/certificates/a.test.key read_only: true - type: bind source: ./certificates/rootCA.crt @@ -28,12 +28,12 @@ services: interval: 5s retries: 20 - misskey.a.local: + misskey.a.test: image: node:20 depends_on: - db.a.local: + db.a.test: condition: service_healthy - redis.local: + redis.test: condition: service_healthy networks: - internal_network_a @@ -121,7 +121,7 @@ services: interval: 5s retries: 20 - db.a.local: + db.a.test: image: postgres:15-alpine networks: - internal_network_a diff --git a/packages/backend/test-federation/compose.b.yml b/packages/backend/test-federation/compose.b.yml index 7dddac905c21..418ff0bb7c31 100644 --- a/packages/backend/test-federation/compose.b.yml +++ b/packages/backend/test-federation/compose.b.yml @@ -1,23 +1,23 @@ services: - b.local: + b.test: image: nginx:1.27 depends_on: - misskey.b.local: + misskey.b.test: condition: service_healthy networks: - internal_network_b volumes: - type: bind - source: ./.config/b.local.conf - target: /etc/nginx/conf.d/b.local.conf + source: ./.config/b.test.conf + target: /etc/nginx/conf.d/b.test.conf read_only: true - type: bind - source: ./certificates/b.local.crt - target: /etc/nginx/certificates/b.local.crt + source: ./certificates/b.test.crt + target: /etc/nginx/certificates/b.test.crt read_only: true - type: bind - source: ./certificates/b.local.key - target: /etc/nginx/certificates/b.local.key + source: ./certificates/b.test.key + target: /etc/nginx/certificates/b.test.key read_only: true - type: bind source: ./certificates/rootCA.crt @@ -28,15 +28,15 @@ services: interval: 5s retries: 20 - misskey.b.local: + misskey.b.test: image: node:20 depends_on: - db.b.local: + db.b.test: condition: service_healthy - redis.local: + redis.test: condition: service_healthy # avoid conflict for installing dependencies - misskey.a.local: + misskey.a.test: condition: service_healthy networks: - internal_network_b @@ -124,7 +124,7 @@ services: interval: 5s retries: 20 - db.b.local: + db.b.test: image: postgres:15-alpine networks: - internal_network_b diff --git a/packages/backend/test-federation/compose.override.yaml b/packages/backend/test-federation/compose.override.yaml index a35b540ca7d8..680e0d87d278 100644 --- a/packages/backend/test-federation/compose.override.yaml +++ b/packages/backend/test-federation/compose.override.yaml @@ -28,16 +28,16 @@ services: source: node_modules_backend target: /misskey/packages/backend/node_modules - redis.local: + redis.test: networks: - internal_network_a - internal_network_b - a.local: + a.test: networks: - internal_network - misskey.a.local: + misskey.a.test: networks: - external_network - internal_network @@ -55,11 +55,11 @@ services: source: node_modules_misskey-reversi target: /misskey/packages/misskey-reversi/node_modules - b.local: + b.test: networks: - internal_network - misskey.b.local: + misskey.b.test: networks: - external_network - internal_network diff --git a/packages/backend/test-federation/compose.yml b/packages/backend/test-federation/compose.yml index 27a018847fd6..efe08c82fdc6 100644 --- a/packages/backend/test-federation/compose.yml +++ b/packages/backend/test-federation/compose.yml @@ -6,9 +6,9 @@ services: tester: image: node:20 depends_on: - a.local: + a.test: condition: service_healthy - b.local: + b.test: condition: service_healthy environment: - NODE_ENV=production @@ -61,9 +61,9 @@ services: daemon: image: node:20 depends_on: - misskey.a.local: + misskey.a.test: condition: service_healthy - misskey.b.local: + misskey.b.test: condition: service_healthy environment: - NODE_ENV=production @@ -97,7 +97,7 @@ services: node ./packages/backend/test-federation/daemon.js " - redis.local: + redis.test: image: redis:7-alpine volumes: - type: bind diff --git a/packages/backend/test-federation/daemon.ts b/packages/backend/test-federation/daemon.ts index 8eb275d4c3b8..e0fb937db1fd 100644 --- a/packages/backend/test-federation/daemon.ts +++ b/packages/backend/test-federation/daemon.ts @@ -26,11 +26,11 @@ console.log('Daemon started running'); { const redisClient = new Redis({ - host: 'redis.local', + host: 'redis.test', }); setInterval(() => { - purgeLimit('a.local', redisClient); - purgeLimit('b.local', redisClient); + purgeLimit('a.test', redisClient); + purgeLimit('b.test', redisClient); }, 1000); } diff --git a/packages/backend/test-federation/generate_certificates.sh b/packages/backend/test-federation/generate_certificates.sh index eae68ba83907..a36914cd849e 100644 --- a/packages/backend/test-federation/generate_certificates.sh +++ b/packages/backend/test-federation/generate_certificates.sh @@ -28,5 +28,5 @@ function generate { -days 500 } -generate a.local -generate b.local +generate a.test +generate b.test diff --git a/packages/backend/test-federation/test/blocking.test.ts b/packages/backend/test-federation/test/blocking.test.ts index f5b9afc39f0c..c249f9fed1a9 100644 --- a/packages/backend/test-federation/test/blocking.test.ts +++ b/packages/backend/test-federation/test/blocking.test.ts @@ -6,18 +6,18 @@ const [ [, aAdminClient], [, bAdminClient], ] = await Promise.all([ - fetchAdmin('a.local'), - fetchAdmin('b.local'), + fetchAdmin('a.test'), + fetchAdmin('b.test'), ]); describe('Blocking', () => { test('Check follow', async () => { - const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); - const [bob, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + const [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); const [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.local/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.local/@${aliceUsername}`, bobClient), + resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), ]); await test('Cannot follow if blocked', async () => { @@ -87,12 +87,12 @@ describe('Blocking', () => { }); test('Check reply', async () => { - const [, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); - const [, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + const [, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + const [, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); const [bobInAServer] = await Promise.all([ - resolveRemoteUser(`https://b.local/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.local/@${aliceUsername}`, bobClient), + resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), ]); await test('Cannot reply if blocked', async () => { @@ -100,7 +100,7 @@ describe('Blocking', () => { await new Promise(resolve => setTimeout(resolve, 1000)); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); await rejects( async () => await bobClient.request('notes/create', { text: 'b', replyId: resolvedNote.id }), (err: any) => { @@ -115,20 +115,20 @@ describe('Blocking', () => { await new Promise(resolve => setTimeout(resolve, 1000)); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); const reply = (await bobClient.request('notes/create', { text: 'b', replyId: resolvedNote.id })).createdNote; - await resolveRemoteNote(`https://b.local/notes/${reply.id}`, aliceClient); + await resolveRemoteNote(`https://b.test/notes/${reply.id}`, aliceClient); }); }); test('Check reaction', async () => { - const [, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); - const [, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + const [, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + const [, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); const [bobInAServer] = await Promise.all([ - resolveRemoteUser(`https://b.local/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.local/@${aliceUsername}`, bobClient), + resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), ]); await test('Cannot reply if blocked', async () => { @@ -136,7 +136,7 @@ describe('Blocking', () => { await new Promise(resolve => setTimeout(resolve, 1000)); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); await rejects( async () => await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: '😅' }), (err: any) => { @@ -153,7 +153,7 @@ describe('Blocking', () => { await new Promise(resolve => setTimeout(resolve, 1000)); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); // TODO: why still being blocked? await rejects( @@ -170,7 +170,7 @@ describe('Blocking', () => { await new Promise(resolve => setTimeout(resolve, 1000)); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: '😅' }); const _note = await aliceClient.request('notes/show', { noteId: note.id }); diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index d204edd88a17..abbe1fc2688b 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -6,25 +6,25 @@ const [ [, aAdminClient], [, bAdminClient], ] = await Promise.all([ - fetchAdmin('a.local'), - fetchAdmin('b.local'), + fetchAdmin('a.test'), + fetchAdmin('b.test'), ]); describe('Drive', () => { - describe('Upload image in a.local and resolve from b.local', async () => { - const [uploader, uploaderClient] = await createAccount('a.local', aAdminClient); + describe('Upload image in a.test and resolve from b.test', async () => { + const [uploader, uploaderClient] = await createAccount('a.test', aAdminClient); - const image = await uploadFile('a.local', '../../test/resources/192.jpg', uploader.i); + const image = await uploadFile('a.test', '../../test/resources/192.jpg', uploader.i); const noteWithImage = (await uploaderClient.request('notes/create', { fileIds: [image.id] })).createdNote; - const uri = `https://a.local/notes/${noteWithImage.id}`; + const uri = `https://a.test/notes/${noteWithImage.id}`; const noteInBServer = await resolveRemoteNote(uri, bAdminClient); assert(noteInBServer.files != null); strictEqual(noteInBServer.files.length, 1); const imageInBServer = noteInBServer.files[0]; await test('Check consistency of DriveFile', () => { - // console.log(`a.local: ${JSON.stringify(image, null, '\t')}`); - // console.log(`b.local: ${JSON.stringify(imageInBServer, null, '\t')}`); + // console.log(`a.test: ${JSON.stringify(image, null, '\t')}`); + // console.log(`b.test: ${JSON.stringify(imageInBServer, null, '\t')}`); deepStrictEqualWithExcludedFields(image, imageInBServer, [ 'id', @@ -47,8 +47,8 @@ describe('Drive', () => { }); await test('Update', async () => { - // console.log(`a.local: ${JSON.stringify(updatedImage, null, '\t')}`); - // console.log(`b.local: ${JSON.stringify(updatedImageInBServer, null, '\t')}`); + // console.log(`a.test: ${JSON.stringify(updatedImage, null, '\t')}`); + // console.log(`b.test: ${JSON.stringify(updatedImageInBServer, null, '\t')}`); // FIXME: not updated with `drive/files/update` strictEqual(updatedImage.isSensitive, true); @@ -58,14 +58,14 @@ describe('Drive', () => { }); const noteWithUpdatedImage = (await uploaderClient.request('notes/create', { fileIds: [updatedImage.id] })).createdNote; - const uriUpdated = `https://a.local/notes/${noteWithUpdatedImage.id}`; + const uriUpdated = `https://a.test/notes/${noteWithUpdatedImage.id}`; const noteWithUpdatedImageInBServer = await resolveRemoteNote(uriUpdated, bAdminClient); assert(noteWithUpdatedImageInBServer.files != null); strictEqual(noteWithUpdatedImageInBServer.files.length, 1); const reupdatedImageInBServer = noteWithUpdatedImageInBServer.files[0]; await test('Re-update with attaching to Note', async () => { - // console.log(`b.local: ${JSON.stringify(reupdatedImageInBServer, null, '\t')}`); + // console.log(`b.test: ${JSON.stringify(reupdatedImageInBServer, null, '\t')}`); // `isSensitive` is updated strictEqual(reupdatedImageInBServer.isSensitive, true); diff --git a/packages/backend/test-federation/test/move.test.ts b/packages/backend/test-federation/test/move.test.ts index 15c6fd24d4fd..567ce04eba61 100644 --- a/packages/backend/test-federation/test/move.test.ts +++ b/packages/backend/test-federation/test/move.test.ts @@ -6,31 +6,31 @@ const [ [, aAdminClient], [, bAdminClient], ] = await Promise.all([ - fetchAdmin('a.local'), - fetchAdmin('b.local'), + fetchAdmin('a.test'), + fetchAdmin('b.test'), ]); describe('Move', async () => { await test('Minimum move', async () => { - const [, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); - const [, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + const [, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + const [, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); - await bobClient.request('i/update', { alsoKnownAs: [`@${aliceUsername}@a.local`] }); - await aliceClient.request('i/move', { moveToAccount: `@${bobUsername}@b.local` }); + await bobClient.request('i/update', { alsoKnownAs: [`@${aliceUsername}@a.test`] }); + await aliceClient.request('i/move', { moveToAccount: `@${bobUsername}@b.test` }); }); /** @see https://github.com/misskey-dev/misskey/issues/11320 */ await test('Following relation is transferred after move', async () => { - const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); - const [bob, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); - const [carol, carolClient, { username: carolUsername }] = await createAccount('a.local', aAdminClient); + const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + const [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + const [carol, carolClient, { username: carolUsername }] = await createAccount('a.test', aAdminClient); - // Follow @carol@a.local ==> @alice@a.local + // Follow @carol@a.test ==> @alice@a.test await carolClient.request('following/create', { userId: alice.id }); - // Move @alice@a.local ==> @bob@b.local - await bobClient.request('i/update', { alsoKnownAs: [`@${aliceUsername}@a.local`] }); - await aliceClient.request('i/move', { moveToAccount: `@${bobUsername}@b.local` }); + // Move @alice@a.test ==> @bob@b.test + await bobClient.request('i/update', { alsoKnownAs: [`@${aliceUsername}@a.test`] }); + await aliceClient.request('i/move', { moveToAccount: `@${bobUsername}@b.test` }); await new Promise(resolve => setTimeout(resolve, 3000)); await test('Check from follower', async () => { @@ -39,7 +39,7 @@ describe('Move', async () => { const followees = following.map(({ followee }) => followee); assert(followees.every(followee => followee != null)); assert(followees.some(({ id, url }) => id === alice.id && url === null)); - assert(followees.some(({ url }) => url === `https://b.local/@${bobUsername}`)); + assert(followees.some(({ url }) => url === `https://b.test/@${bobUsername}`)); }); await test('Check from followee', async () => { @@ -47,7 +47,7 @@ describe('Move', async () => { strictEqual(followers.length, 1); const follower = followers[0].follower; assert(follower != null); - strictEqual(follower.url, `https://a.local/@${carolUsername}`); + strictEqual(follower.url, `https://a.test/@${carolUsername}`); }); }); }); diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index 114cbdc3f0a4..7951ac9bea67 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -6,22 +6,22 @@ const [ [, aAdminClient], [, bAdminClient], ] = await Promise.all([ - fetchAdmin('a.local'), - fetchAdmin('b.local'), + fetchAdmin('a.test'), + fetchAdmin('b.test'), ]); describe('Note', async () => { - const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); - const [, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + const [, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); const [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.local/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.local/@${aliceUsername}`, bobClient), + resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), ]); describe('Note content', () => { test('Consistency of Public Note', async () => { - const image = await uploadFile('a.local', '../../test/resources/192.jpg', alice.i); + const image = await uploadFile('a.test', '../../test/resources/192.jpg', alice.i); const note = (await aliceClient.request('notes/create', { text: 'I am Alice!', fileIds: [image.id], @@ -32,7 +32,7 @@ describe('Note', async () => { }, })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); deepStrictEqualWithExcludedFields(note, resolvedNote, [ 'id', 'emojis', @@ -59,7 +59,7 @@ describe('Note', async () => { const replyedNote = await aliceClient.request('notes/show', { noteId: _replyedNote.id }); strictEqual(replyedNote.repliesCount, 1); - const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); deepStrictEqualWithExcludedFields(note, resolvedNote, [ 'id', 'emojis', @@ -101,7 +101,7 @@ describe('Note', async () => { renoteId: renotedNote.id, })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); deepStrictEqualWithExcludedFields(note, resolvedNote, [ 'id', 'emojis', @@ -129,7 +129,7 @@ describe('Note', async () => { test('localOnly', async () => { const note = (await aliceClient.request('notes/create', { text: 'a', localOnly: true })).createdNote; rejects( - async () => await bobClient.request('ap/show', { uri: `https://a.local/notes/${note.id}` }), + async () => await bobClient.request('ap/show', { uri: `https://a.test/notes/${note.id}` }), (err: any) => { /** * FIXME: this error is not handled @@ -145,7 +145,7 @@ describe('Note', async () => { describe('Reaction', () => { test('Consistency of reaction', async () => { const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.local/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); const reaction = '😅'; await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction }); await new Promise(resolve => setTimeout(resolve, 1000)); diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index a10bbaa2ad0c..6c3307b398d3 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -6,30 +6,30 @@ const [ [, aAdminClient], [, bAdminClient], ] = await Promise.all([ - fetchAdmin('a.local'), - fetchAdmin('b.local'), + fetchAdmin('a.test'), + fetchAdmin('b.test'), ]); describe('User', () => { describe('Profile', async () => { describe('Consistency of profile', async () => { - const [alice] = await createAccount('a.local', aAdminClient); + const [alice] = await createAccount('a.test', aAdminClient); const [ [, aliceWatcherClient], [, aliceWatcherInBServerClient], ] = await Promise.all([ - createAccount('a.local', aAdminClient), - createAccount('b.local', bAdminClient), + createAccount('a.test', aAdminClient), + createAccount('b.test', bAdminClient), ]); const aliceInAServer = await aliceWatcherClient.request('users/show', { userId: alice.id }); - const resolved = await resolveRemoteUser(`https://a.local/@${aliceInAServer.username}`, aliceWatcherInBServerClient); + const resolved = await resolveRemoteUser(`https://a.test/@${aliceInAServer.username}`, aliceWatcherInBServerClient); const aliceInBServer = await aliceWatcherInBServerClient.request('users/show', { userId: resolved.id }); - // console.log(`a.local: ${JSON.stringify(aliceInAServer, null, '\t')}`); - // console.log(`b.local: ${JSON.stringify(aliceInBServer, null, '\t')}`); + // console.log(`a.test: ${JSON.stringify(aliceInAServer, null, '\t')}`); + // console.log(`b.test: ${JSON.stringify(aliceInBServer, null, '\t')}`); deepStrictEqualWithExcludedFields(aliceInAServer, aliceInBServer, [ 'id', @@ -47,15 +47,15 @@ describe('User', () => { }); describe('Follow / Unfollow', async () => { - const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); - const [bob, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + const [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); const [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.local/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.local/@${aliceUsername}`, bobClient), + resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), ]); - await describe('Follow a.local ==> b.local', async () => { + await describe('Follow a.test ==> b.test', async () => { before(async () => { await aliceClient.request('following/create', { userId: bobInAServer.id }); @@ -79,7 +79,7 @@ describe('User', () => { }); }); - await describe('Unfollow a.local ==> b.local', async () => { + await describe('Unfollow a.test ==> b.test', async () => { before(async () => { await aliceClient.request('following/delete', { userId: bobInAServer.id }); @@ -105,12 +105,12 @@ describe('User', () => { }); describe('Follow requests', async () => { - const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.local', aAdminClient); - const [bob, bobClient, { username: bobUsername }] = await createAccount('b.local', bAdminClient); + const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + const [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); const [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.local/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.local/@${aliceUsername}`, bobClient), + resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), ]); await aliceClient.request('i/update', { isLocked: true }); From da09a464857f561d0e7c2341a3c24294dcd4d1a7 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Fri, 20 Sep 2024 23:59:52 +0900 Subject: [PATCH 023/111] chore: shorter purge interval --- packages/backend/test-federation/daemon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test-federation/daemon.ts b/packages/backend/test-federation/daemon.ts index e0fb937db1fd..f6dfa1699da7 100644 --- a/packages/backend/test-federation/daemon.ts +++ b/packages/backend/test-federation/daemon.ts @@ -32,5 +32,5 @@ console.log('Daemon started running'); setInterval(() => { purgeLimit('a.test', redisClient); purgeLimit('b.test', redisClient); - }, 1000); + }, 200); } From 2d280f57b50d9e423283932c214bdaae484f4728 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sat, 21 Sep 2024 00:03:49 +0900 Subject: [PATCH 024/111] fix(ci): change TLD --- .github/workflows/test-federation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml index a95f6f2d74da..183141c7564b 100644 --- a/.github/workflows/test-federation.yml +++ b/.github/workflows/test-federation.yml @@ -45,7 +45,7 @@ jobs: cd packages/backend/test-federation cp ./.env.example ./.env bash ./generate_certificates.sh - sudo chmod 644 ./certificates/*.local.key + sudo chmod 644 ./certificates/*.test.key - name: Start servers # https://github.com/docker/compose/issues/1294#issuecomment-374847206 run: | From 33ae401c63342eb9cf099fbf9eb4d93544938ff0 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sat, 21 Sep 2024 00:10:20 +0900 Subject: [PATCH 025/111] refactor: delete trivial comment --- packages/backend/test-federation/test/user.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index 6c3307b398d3..497a054ccf33 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -59,7 +59,6 @@ describe('User', () => { before(async () => { await aliceClient.request('following/create', { userId: bobInAServer.id }); - // wait for 1 secound await new Promise(resolve => setTimeout(resolve, 1000)); }); @@ -83,7 +82,6 @@ describe('User', () => { before(async () => { await aliceClient.request('following/delete', { userId: bobInAServer.id }); - // wait for 1 secound await new Promise(resolve => setTimeout(resolve, 1000)); }); From 51bb1af62a8366bac13deb71e39a72ab5ac4dac1 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sat, 21 Sep 2024 00:25:28 +0900 Subject: [PATCH 026/111] test(user): isCat --- .../backend/test-federation/test/user.test.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index 497a054ccf33..4dc734d32b4d 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -44,6 +44,26 @@ describe('User', () => { 'publicReactions', ]); }); + + describe('isCat is federated', async () => { + const [, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + const [, bobClient] = await createAccount('b.test', bAdminClient); + const aliceInBServer = await resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient); + await test('Not isCat for default', () => { + strictEqual(aliceInBServer.isCat, false); + }); + + await test('Becoming a cat is sent to their followers', async () => { + await bobClient.request('following/create', { userId: aliceInBServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + await aliceClient.request('i/update', { isCat: true }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + const res = await bobClient.request('users/show', { userId: aliceInBServer.id }); + strictEqual(res.isCat, true); + }); + }); }); describe('Follow / Unfollow', async () => { From c8e416d14f9e3531348ca53c8db4c44d5c7f01b5 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 11:31:40 +0900 Subject: [PATCH 027/111] chore: use jest --- packages/backend/jest.config.fed.cjs | 13 ++ packages/backend/package.json | 2 + .../test-federation/compose.override.yaml | 9 +- packages/backend/test-federation/compose.yml | 24 ++- .../test-federation/test/blocking.test.ts | 86 ++++++----- .../test-federation/test/drive.test.ts | 118 +++++++++------ .../backend/test-federation/test/move.test.ts | 40 ++--- .../backend/test-federation/test/note.test.ts | 22 ++- .../backend/test-federation/test/user.test.ts | 143 +++++++++++------- packages/shared/eslint.config.js | 7 + 10 files changed, 293 insertions(+), 171 deletions(-) create mode 100644 packages/backend/jest.config.fed.cjs diff --git a/packages/backend/jest.config.fed.cjs b/packages/backend/jest.config.fed.cjs new file mode 100644 index 000000000000..fae187bc23f3 --- /dev/null +++ b/packages/backend/jest.config.fed.cjs @@ -0,0 +1,13 @@ +/* + * For a detailed explanation regarding each configuration property and type check, visit: + * https://jestjs.io/docs/en/configuration.html + */ + +const base = require('./jest.config.cjs'); + +module.exports = { + ...base, + testMatch: [ + '/test-federation/test/**/*.test.ts', + ], +}; diff --git a/packages/backend/package.json b/packages/backend/package.json index 7a886b5d8824..f552d171b8e8 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -25,11 +25,13 @@ "lint": "pnpm typecheck && pnpm eslint", "jest": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --config jest.config.unit.cjs", "jest:e2e": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --config jest.config.e2e.cjs", + "jest:fed": "node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --config jest.config.fed.cjs", "jest-and-coverage": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --config jest.config.unit.cjs", "jest-and-coverage:e2e": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --config jest.config.e2e.cjs", "jest-clear": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --clearCache", "test": "pnpm jest", "test:e2e": "pnpm build && pnpm build:test && pnpm jest:e2e", + "test:fed": "pnpm jest:fed", "test-and-coverage": "pnpm jest-and-coverage", "test-and-coverage:e2e": "pnpm build && pnpm build:test && pnpm jest-and-coverage:e2e", "generate-api-json": "node ./scripts/generate_api_json.js" diff --git a/packages/backend/test-federation/compose.override.yaml b/packages/backend/test-federation/compose.override.yaml index 680e0d87d278..875af2f2ec11 100644 --- a/packages/backend/test-federation/compose.override.yaml +++ b/packages/backend/test-federation/compose.override.yaml @@ -6,13 +6,13 @@ services: ipv4_address: $TESTER_IP_ADDRESS volumes: - type: volume - source: node_modules + source: node_modules_dev target: /misskey/node_modules - type: volume - source: node_modules_backend + source: node_modules_backend_dev target: /misskey/packages/backend/node_modules - type: volume - source: node_modules_misskey-js + source: node_modules_misskey-js_dev target: /misskey/packages/misskey-js/node_modules daemon: @@ -94,6 +94,9 @@ networks: volumes: node_modules: + node_modules_dev: node_modules_backend: + node_modules_backend_dev: node_modules_misskey-js: + node_modules_misskey-js_dev: node_modules_misskey-reversi: diff --git a/packages/backend/test-federation/compose.yml b/packages/backend/test-federation/compose.yml index efe08c82fdc6..e98b0fb772ca 100644 --- a/packages/backend/test-federation/compose.yml +++ b/packages/backend/test-federation/compose.yml @@ -11,7 +11,7 @@ services: b.test: condition: service_healthy environment: - - NODE_ENV=production + - NODE_ENV=development - NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/rootCA.crt volumes: - type: bind @@ -23,8 +23,16 @@ services: target: /misskey/packages/backend/test/resources read_only: true - type: bind - source: ./built - target: /misskey/packages/backend/test-federation + source: ./test + target: /misskey/packages/backend/test-federation/test + read_only: true + - type: bind + source: ../jest.config.cjs + target: /misskey/packages/backend/jest.config.cjs + read_only: true + - type: bind + source: ../jest.config.fed.cjs + target: /misskey/packages/backend/jest.config.fed.cjs read_only: true - type: bind source: ../../misskey-js/built @@ -51,12 +59,14 @@ services: target: /usr/local/share/ca-certificates/rootCA.crt read_only: true working_dir: /misskey - command: > - bash -c " + entrypoint: > + bash -c ' corepack enable && corepack prepare + pnpm -F misskey-js i pnpm -F backend i - node --test "./packages/backend/test-federation/test/*.test.js" - " + exec "$0" "$@" + ' + command: pnpm -F backend test:fed daemon: image: node:20 diff --git a/packages/backend/test-federation/test/blocking.test.ts b/packages/backend/test-federation/test/blocking.test.ts index c249f9fed1a9..c69762a86d4c 100644 --- a/packages/backend/test-federation/test/blocking.test.ts +++ b/packages/backend/test-federation/test/blocking.test.ts @@ -1,5 +1,5 @@ -import test, { describe } from 'node:test'; import { deepStrictEqual, rejects, strictEqual } from 'node:assert'; +import * as Misskey from 'misskey-js'; import { createAccount, fetchAdmin, resolveRemoteNote, resolveRemoteUser } from './utils.js'; const [ @@ -11,16 +11,22 @@ const [ ]); describe('Blocking', () => { - test('Check follow', async () => { - const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - const [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); - - const [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), - ]); + describe('Check follow', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + ]); + }); - await test('Cannot follow if blocked', async () => { + test('Cannot follow if blocked', async () => { await aliceClient.request('blocking/create', { userId: bobInAServer.id }); await new Promise(resolve => setTimeout(resolve, 1000)); await rejects( @@ -38,7 +44,7 @@ describe('Blocking', () => { }); // FIXME: this is invalid case - await test('Cannot follow even if unblocked', async () => { + test('Cannot follow even if unblocked', async () => { // unblock here await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); await new Promise(resolve => setTimeout(resolve, 1000)); @@ -53,7 +59,7 @@ describe('Blocking', () => { ); }); - await test.skip('Can follow if unblocked', async () => { + test.skip('Can follow if unblocked', async () => { await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); await new Promise(resolve => setTimeout(resolve, 1000)); @@ -66,8 +72,8 @@ describe('Blocking', () => { strictEqual(followers.length, 1); }); - await test.skip('Remove follower when block them', async () => { - await test('before block', async () => { + test.skip('Remove follower when block them', async () => { + test('before block', async () => { const following = await bobClient.request('users/following', { userId: bob.id }); strictEqual(following.length, 1); const followers = await aliceClient.request('users/followers', { userId: alice.id }); @@ -77,7 +83,7 @@ describe('Blocking', () => { await aliceClient.request('blocking/create', { userId: bobInAServer.id }); await new Promise(resolve => setTimeout(resolve, 1000)); - await test('after block', async () => { + test('after block', async () => { const following = await bobClient.request('users/following', { userId: bob.id }); strictEqual(following.length, 0); const followers = await aliceClient.request('users/followers', { userId: alice.id }); @@ -86,16 +92,22 @@ describe('Blocking', () => { }); }); - test('Check reply', async () => { - const [, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - const [, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + describe('Check reply', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); - const [bobInAServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), - ]); + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + ]); + }); - await test('Cannot reply if blocked', async () => { + test('Cannot reply if blocked', async () => { await aliceClient.request('blocking/create', { userId: bobInAServer.id }); await new Promise(resolve => setTimeout(resolve, 1000)); @@ -110,7 +122,7 @@ describe('Blocking', () => { ); }); - await test('Can reply if unblocked', async () => { + test('Can reply if unblocked', async () => { await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); await new Promise(resolve => setTimeout(resolve, 1000)); @@ -122,16 +134,22 @@ describe('Blocking', () => { }); }); - test('Check reaction', async () => { - const [, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - const [, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + describe('Check reaction', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); - const [bobInAServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), - ]); + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + ]); + }); - await test('Cannot reply if blocked', async () => { + test('Cannot reply if blocked', async () => { await aliceClient.request('blocking/create', { userId: bobInAServer.id }); await new Promise(resolve => setTimeout(resolve, 1000)); @@ -147,7 +165,7 @@ describe('Blocking', () => { }); // FIXME: this is invalid case - await test('Cannot reply even if unblocked', async () => { + test('Cannot reply even if unblocked', async () => { // unblock here await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); await new Promise(resolve => setTimeout(resolve, 1000)); @@ -165,7 +183,7 @@ describe('Blocking', () => { ); }); - await test.skip('Can reply if unblocked', async () => { + test.skip('Can reply if unblocked', async () => { await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); await new Promise(resolve => setTimeout(resolve, 1000)); diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index abbe1fc2688b..589d60084caf 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -1,5 +1,5 @@ import assert, { strictEqual } from 'node:assert'; -import test, { describe } from 'node:test'; +import * as Misskey from 'misskey-js'; import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, uploadFile } from './utils.js'; const [ @@ -11,66 +11,88 @@ const [ ]); describe('Drive', () => { - describe('Upload image in a.test and resolve from b.test', async () => { - const [uploader, uploaderClient] = await createAccount('a.test', aAdminClient); - - const image = await uploadFile('a.test', '../../test/resources/192.jpg', uploader.i); - const noteWithImage = (await uploaderClient.request('notes/create', { fileIds: [image.id] })).createdNote; - const uri = `https://a.test/notes/${noteWithImage.id}`; - const noteInBServer = await resolveRemoteNote(uri, bAdminClient); - assert(noteInBServer.files != null); - strictEqual(noteInBServer.files.length, 1); - const imageInBServer = noteInBServer.files[0]; - - await test('Check consistency of DriveFile', () => { + describe('Upload image in a.test and resolve from b.test', () => { + let uploader: Misskey.entities.SigninResponse, uploaderClient: Misskey.api.APIClient; + + beforeAll(async () => { + [uploader, uploaderClient] = await createAccount('a.test', aAdminClient); + }); + + let image: Misskey.entities.DriveFile, imageInBServer: Misskey.entities.DriveFile; + + describe('Upload', () => { + beforeAll(async () => { + image = await uploadFile('a.test', '../../test/resources/192.jpg', uploader.i); + const noteWithImage = (await uploaderClient.request('notes/create', { fileIds: [image.id] })).createdNote; + const uri = `https://a.test/notes/${noteWithImage.id}`; + const noteInBServer = await resolveRemoteNote(uri, bAdminClient); + assert(noteInBServer.files != null); + strictEqual(noteInBServer.files.length, 1); + imageInBServer = noteInBServer.files[0]; + }); + + test('Check consistency of DriveFile', () => { // console.log(`a.test: ${JSON.stringify(image, null, '\t')}`); // console.log(`b.test: ${JSON.stringify(imageInBServer, null, '\t')}`); - deepStrictEqualWithExcludedFields(image, imageInBServer, [ - 'id', - 'createdAt', - 'size', - 'url', - 'thumbnailUrl', - 'userId', - ]); + deepStrictEqualWithExcludedFields(image, imageInBServer, [ + 'id', + 'createdAt', + 'size', + 'url', + 'thumbnailUrl', + 'userId', + ]); + }); }); - const updatedImage = await uploaderClient.request('drive/files/update', { - fileId: image.id, - name: 'updated_192.jpg', - isSensitive: true, - }); + let updatedImage: Misskey.entities.DriveFile, updatedImageInBServer: Misskey.entities.DriveFile; - const updatedImageInBServer = await bAdminClient.request('drive/files/show', { - fileId: imageInBServer.id, - }); + describe('Update', () => { + beforeAll(async () => { + updatedImage = await uploaderClient.request('drive/files/update', { + fileId: image.id, + name: 'updated_192.jpg', + isSensitive: true, + }); - await test('Update', async () => { - // console.log(`a.test: ${JSON.stringify(updatedImage, null, '\t')}`); + updatedImageInBServer = await bAdminClient.request('drive/files/show', { + fileId: imageInBServer.id, + }); + }); + + test('Check consistency', () => { + // console.log(`a.test: ${JSON.stringify(updatedImage, null, '\t')}`); // console.log(`b.test: ${JSON.stringify(updatedImageInBServer, null, '\t')}`); - // FIXME: not updated with `drive/files/update` - strictEqual(updatedImage.isSensitive, true); - strictEqual(updatedImage.name, 'updated_192.jpg'); - strictEqual(updatedImageInBServer.isSensitive, false); - strictEqual(updatedImageInBServer.name, '192.jpg'); + // FIXME: not updated with `drive/files/update` + strictEqual(updatedImage.isSensitive, true); + strictEqual(updatedImage.name, 'updated_192.jpg'); + strictEqual(updatedImageInBServer.isSensitive, false); + strictEqual(updatedImageInBServer.name, '192.jpg'); + }); }); - const noteWithUpdatedImage = (await uploaderClient.request('notes/create', { fileIds: [updatedImage.id] })).createdNote; - const uriUpdated = `https://a.test/notes/${noteWithUpdatedImage.id}`; - const noteWithUpdatedImageInBServer = await resolveRemoteNote(uriUpdated, bAdminClient); - assert(noteWithUpdatedImageInBServer.files != null); - strictEqual(noteWithUpdatedImageInBServer.files.length, 1); - const reupdatedImageInBServer = noteWithUpdatedImageInBServer.files[0]; + let reupdatedImageInBServer: Misskey.entities.DriveFile; + + describe('Re-update with attaching to Note', () => { + beforeAll(async () => { + const noteWithUpdatedImage = (await uploaderClient.request('notes/create', { fileIds: [updatedImage.id] })).createdNote; + const uriUpdated = `https://a.test/notes/${noteWithUpdatedImage.id}`; + const noteWithUpdatedImageInBServer = await resolveRemoteNote(uriUpdated, bAdminClient); + assert(noteWithUpdatedImageInBServer.files != null); + strictEqual(noteWithUpdatedImageInBServer.files.length, 1); + reupdatedImageInBServer = noteWithUpdatedImageInBServer.files[0]; + }); - await test('Re-update with attaching to Note', async () => { - // console.log(`b.test: ${JSON.stringify(reupdatedImageInBServer, null, '\t')}`); + test('Check consistency', () => { + // console.log(`b.test: ${JSON.stringify(reupdatedImageInBServer, null, '\t')}`); - // `isSensitive` is updated - strictEqual(reupdatedImageInBServer.isSensitive, true); - // FIXME: but `name` is not updated - strictEqual(reupdatedImageInBServer.name, '192.jpg'); + // `isSensitive` is updated + strictEqual(reupdatedImageInBServer.isSensitive, true); + // FIXME: but `name` is not updated + strictEqual(reupdatedImageInBServer.name, '192.jpg'); + }); }); }); }); diff --git a/packages/backend/test-federation/test/move.test.ts b/packages/backend/test-federation/test/move.test.ts index 567ce04eba61..9e0201ded3b0 100644 --- a/packages/backend/test-federation/test/move.test.ts +++ b/packages/backend/test-federation/test/move.test.ts @@ -1,5 +1,5 @@ -import test, { describe } from 'node:test'; import assert, { strictEqual } from 'node:assert'; +import * as Misskey from 'misskey-js'; import { createAccount, fetchAdmin } from './utils.js'; const [ @@ -10,8 +10,8 @@ const [ fetchAdmin('b.test'), ]); -describe('Move', async () => { - await test('Minimum move', async () => { +describe('Move', () => { + test('Minimum move', async () => { const [, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); const [, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); @@ -20,20 +20,26 @@ describe('Move', async () => { }); /** @see https://github.com/misskey-dev/misskey/issues/11320 */ - await test('Following relation is transferred after move', async () => { - const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - const [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); - const [carol, carolClient, { username: carolUsername }] = await createAccount('a.test', aAdminClient); - - // Follow @carol@a.test ==> @alice@a.test - await carolClient.request('following/create', { userId: alice.id }); - - // Move @alice@a.test ==> @bob@b.test - await bobClient.request('i/update', { alsoKnownAs: [`@${aliceUsername}@a.test`] }); - await aliceClient.request('i/move', { moveToAccount: `@${bobUsername}@b.test` }); - await new Promise(resolve => setTimeout(resolve, 3000)); + describe('Following relation is transferred after move', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let carol: Misskey.entities.SigninResponse, carolClient: Misskey.api.APIClient, carolUsername: string; + + beforeAll(async () => { + [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + [carol, carolClient, { username: carolUsername }] = await createAccount('a.test', aAdminClient); + + // Follow @carol@a.test ==> @alice@a.test + await carolClient.request('following/create', { userId: alice.id }); + + // Move @alice@a.test ==> @bob@b.test + await bobClient.request('i/update', { alsoKnownAs: [`@${aliceUsername}@a.test`] }); + await aliceClient.request('i/move', { moveToAccount: `@${bobUsername}@b.test` }); + await new Promise(resolve => setTimeout(resolve, 3000)); + }); - await test('Check from follower', async () => { + test('Check from follower', async () => { const following = await carolClient.request('users/following', { userId: carol.id }); strictEqual(following.length, 2); const followees = following.map(({ followee }) => followee); @@ -42,7 +48,7 @@ describe('Move', async () => { assert(followees.some(({ url }) => url === `https://b.test/@${bobUsername}`)); }); - await test('Check from followee', async () => { + test('Check from followee', async () => { const followers = await bobClient.request('users/followers', { userId: bob.id }); strictEqual(followers.length, 1); const follower = followers[0].follower; diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index 7951ac9bea67..ee5ae7bf16cf 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -1,5 +1,5 @@ -import test, { describe } from 'node:test'; import assert, { rejects, strictEqual } from 'node:assert'; +import * as Misskey from 'misskey-js'; import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, resolveRemoteUser, uploadFile } from './utils.js'; const [ @@ -10,14 +10,20 @@ const [ fetchAdmin('b.test'), ]); -describe('Note', async () => { - const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - const [, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); +describe('Note', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; - const [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), - ]); + beforeAll(async () => { + [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + ]); + }); describe('Note content', () => { test('Consistency of Public Note', async () => { diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index 4dc734d32b4d..ccf739627959 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -1,5 +1,5 @@ import { rejects, strictEqual } from 'node:assert'; -import test, { before, describe } from 'node:test'; +import * as Misskey from 'misskey-js'; import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteUser } from './utils.js'; const [ @@ -11,8 +11,8 @@ const [ ]); describe('User', () => { - describe('Profile', async () => { - describe('Consistency of profile', async () => { + describe('Profile', () => { + test('Consistency of profile', async () => { const [alice] = await createAccount('a.test', aAdminClient); const [ [, aliceWatcherClient], @@ -45,15 +45,26 @@ describe('User', () => { ]); }); - describe('isCat is federated', async () => { - const [, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - const [, bobClient] = await createAccount('b.test', bAdminClient); - const aliceInBServer = await resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient); - await test('Not isCat for default', () => { + describe('isCat is federated', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + ]); + }); + + test('Not isCat for default', () => { strictEqual(aliceInBServer.isCat, false); }); - await test('Becoming a cat is sent to their followers', async () => { + test('Becoming a cat is sent to their followers', async () => { await bobClient.request('following/create', { userId: aliceInBServer.id }); await new Promise(resolve => setTimeout(resolve, 1000)); @@ -66,17 +77,23 @@ describe('User', () => { }); }); - describe('Follow / Unfollow', async () => { - const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - const [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + describe('Follow / Unfollow', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; - const [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), - ]); + beforeAll(async () => { + [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); - await describe('Follow a.test ==> b.test', async () => { - before(async () => { + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + ]); + }); + + describe('Follow a.test ==> b.test', () => { + beforeAll(async () => { await aliceClient.request('following/create', { userId: bobInAServer.id }); await new Promise(resolve => setTimeout(resolve, 1000)); @@ -98,8 +115,8 @@ describe('User', () => { }); }); - await describe('Unfollow a.test ==> b.test', async () => { - before(async () => { + describe('Unfollow a.test ==> b.test', () => { + beforeAll(async () => { await aliceClient.request('following/delete', { userId: bobInAServer.id }); await new Promise(resolve => setTimeout(resolve, 1000)); @@ -122,45 +139,61 @@ describe('User', () => { }); }); - describe('Follow requests', async () => { - const [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - const [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + describe('Follow requests', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; - const [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), - ]); + beforeAll(async () => { + [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); - await aliceClient.request('i/update', { isLocked: true }); + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + ]); - await test('Send follow request from Bob to Alice and cancel', async () => { - await bobClient.request('following/create', { userId: aliceInBServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await aliceClient.request('i/update', { isLocked: true }); + }); - await test('Alice should have a request', async () => { - const requests = await aliceClient.request('following/requests/list', {}); - strictEqual(requests.length, 1); - strictEqual(requests[0].followee.id, alice.id); - strictEqual(requests[0].follower.id, bobInAServer.id); + describe('Send follow request from Bob to Alice and cancel', () => { + describe('Bob sends follow request to Alice', () => { + beforeAll(async () => { + await bobClient.request('following/create', { userId: aliceInBServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + }); + + test('Alice should have a request', async () => { + const requests = await aliceClient.request('following/requests/list', {}); + strictEqual(requests.length, 1); + strictEqual(requests[0].followee.id, alice.id); + strictEqual(requests[0].follower.id, bobInAServer.id); + }); }); - await bobClient.request('following/requests/cancel', { userId: aliceInBServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + describe('Alice cancels it', () => { + beforeAll(async () => { + await bobClient.request('following/requests/cancel', { userId: aliceInBServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + }); - await test('Alice should have no requests', async () => { - const requests = await aliceClient.request('following/requests/list', {}); - strictEqual(requests.length, 0); + test('Alice should have no requests', async () => { + const requests = await aliceClient.request('following/requests/list', {}); + strictEqual(requests.length, 0); + }); }); }); - await test('Send follow request from Bob to Alice and reject', async () => { - await bobClient.request('following/create', { userId: aliceInBServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + describe('Send follow request from Bob to Alice and reject', () => { + beforeAll(async () => { + await bobClient.request('following/create', { userId: aliceInBServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); - await aliceClient.request('following/requests/reject', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await aliceClient.request('following/requests/reject', { userId: bobInAServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + }); - await test('Bob should have no requests', async () => { + test('Bob should have no requests', async () => { await rejects( async () => await bobClient.request('following/requests/cancel', { userId: aliceInBServer.id }), (err: any) => { @@ -170,20 +203,22 @@ describe('User', () => { ); }); - await test('Bob doesn\'t follow Alice', async () => { + test('Bob doesn\'t follow Alice', async () => { const following = await bobClient.request('users/following', { userId: bob.id }); strictEqual(following.length, 0); }); }); - await test('Send follow request from Bob to Alice and accept', async () => { - await bobClient.request('following/create', { userId: aliceInBServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + describe('Send follow request from Bob to Alice and accept', () => { + beforeAll(async () => { + await bobClient.request('following/create', { userId: aliceInBServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); - await aliceClient.request('following/requests/accept', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await aliceClient.request('following/requests/accept', { userId: bobInAServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + }); - await test('Bob follows Alice', async () => { + test('Bob follows Alice', async () => { const following = await bobClient.request('users/following', { userId: bob.id }); strictEqual(following.length, 1); strictEqual(following[0].followeeId, aliceInBServer.id); diff --git a/packages/shared/eslint.config.js b/packages/shared/eslint.config.js index e9d27c4a7288..0368d008c080 100644 --- a/packages/shared/eslint.config.js +++ b/packages/shared/eslint.config.js @@ -6,6 +6,7 @@ export default [ { files: ['**/*.cjs'], languageOptions: { + sourceType: 'commonjs', parserOptions: { sourceType: 'commonjs', }, @@ -25,4 +26,10 @@ export default [ globals: globals.node, }, }, + { + files: ['**/*.js', '**/*.cjs'], + rules: { + '@typescript-eslint/no-var-requires': 'off', + }, + }, ]; From 2ffd6771b7319f813ac6827ebe20f64465642cbd Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 11:33:00 +0900 Subject: [PATCH 028/111] chore: omit logs --- packages/backend/test-federation/test/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 436de27fdcf5..d1649eba0c41 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -18,7 +18,7 @@ export const ADMIN_PARAMS = { username: 'admin', password: 'admin' }; export async function signin(host: string, params: Misskey.entities.SigninRequest): Promise { // wait for a second to prevent hit rate limit await new Promise(resolve => setTimeout(resolve, 1000)); - console.log(`Sign in to @${params.username}@${host} ...`); + // console.log(`Sign in to @${params.username}@${host} ...`); return await (new Misskey.api.APIClient({ origin: `https://${host}`, fetch: (input, init) => fetch(input, { @@ -30,7 +30,7 @@ export async function signin(host: string, params: Misskey.entities.SigninReques }), }).request as Request)('signin', params) .then(res => { - console.log(`Signed in to @${params.username}@${host}`); + // console.log(`Signed in to @${params.username}@${host}`); return res; }) .catch(async err => { @@ -95,7 +95,7 @@ export async function createAccount(host: string, adminClient: Misskey.api.APICl const username = crypto.randomUUID().replaceAll('-', '').substring(0, 20); const password = crypto.randomUUID().replaceAll('-', ''); await adminClient.request('admin/accounts/create', { username, password }); - console.log(`Created an account: @${username}@${host}`); + // console.log(`Created an account: @${username}@${host}`); const signinRes = await signin(host, { username, password }); return [ From 744544a8e9ad8ca9ddfe747982b276300e572a0f Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 12:58:24 +0900 Subject: [PATCH 029/111] chore: add memo --- packages/backend/test-federation/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/backend/test-federation/README.md b/packages/backend/test-federation/README.md index c69a0c611ff4..38b44041c804 100644 --- a/packages/backend/test-federation/README.md +++ b/packages/backend/test-federation/README.md @@ -5,3 +5,8 @@ bash ./generate_certificates.sh pnpm build:fed docker compose up ``` + +For testing a specific file, run following: +```sh +docker compose run --rm tester -- pnpm -F backend test:fed packages/backend/test-federation/test/user.test.ts +``` From 420b711dac07e2de888bdf4336a575bd5e6c1ef7 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 13:06:10 +0900 Subject: [PATCH 030/111] fix(ci): omit unnecessary build --- .github/workflows/test-federation.yml | 1 - packages/backend/package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml index 183141c7564b..9320f41e1c28 100644 --- a/.github/workflows/test-federation.yml +++ b/.github/workflows/test-federation.yml @@ -39,7 +39,6 @@ jobs: corepack enable && corepack prepare pnpm i --frozen-lockfile pnpm build - pnpm -F backend build:fed - name: Setup run: | cd packages/backend/test-federation diff --git a/packages/backend/package.json b/packages/backend/package.json index f552d171b8e8..33184d755e2f 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -16,7 +16,6 @@ "build:test": "swc test-server -d built-test -D --config-file test-server/.swcrc --strip-leading-paths", "watch:swc": "swc src -d built -D -w --strip-leading-paths", "build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json", - "build:fed": "cd test-federation && tsc -p tsconfig.json", "watch": "node ./scripts/watch.mjs", "restart": "pnpm build && pnpm start", "dev": "node ./scripts/dev.mjs", From 0bcde9adaab9ac80585d3f7bb4afe3c76bb0f5f4 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 13:06:47 +0900 Subject: [PATCH 031/111] test: pinning Note --- .../backend/test-federation/test/user.test.ts | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index ccf739627959..3a195d8cc47e 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -1,6 +1,6 @@ import { rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteUser } from './utils.js'; +import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, resolveRemoteUser } from './utils.js'; const [ [, aAdminClient], @@ -75,6 +75,59 @@ describe('User', () => { strictEqual(res.isCat, true); }); }); + + describe('Pinning Notes', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + aliceInBServer = await resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient); + + await bobClient.request('following/create', { userId: aliceInBServer.id }); + }); + + test('Pinning localOnly Note is not delivered', async () => { + const note = (await aliceClient.request('notes/create', { text: 'a', localOnly: true })).createdNote; + await aliceClient.request('i/pin', { noteId: note.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); + strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); + }); + + test('Pinning followers-only Note is not delivered', async () => { + const note = (await aliceClient.request('notes/create', { text: 'a', visibility: 'followers' })).createdNote; + await aliceClient.request('i/pin', { noteId: note.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); + strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); + }); + + let pinnedNote: Misskey.entities.Note; + + test('Pinning normal Note is delivered', async () => { + pinnedNote = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + await aliceClient.request('i/pin', { noteId: pinnedNote.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); + strictEqual(_aliceInBServer.pinnedNoteIds.length, 1); + const pinnedNoteInBServer = await resolveRemoteNote(`https://a.test/notes/${pinnedNote.id}`, bobClient); + strictEqual(_aliceInBServer.pinnedNotes[0].id, pinnedNoteInBServer.id); + }); + + test('Unpinning normal Note is delivered', async () => { + await aliceClient.request('i/unpin', { noteId: pinnedNote.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); + strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); + }); + }); }); describe('Follow / Unfollow', () => { From 6df3b0d5e5cfebd49e5704eeeddf5a9adaf3d05d Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 13:18:51 +0900 Subject: [PATCH 032/111] fix: build daemon in container --- .../backend/test-federation/compose.override.yaml | 4 ++-- packages/backend/test-federation/compose.yml | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/backend/test-federation/compose.override.yaml b/packages/backend/test-federation/compose.override.yaml index 875af2f2ec11..4de162f677bf 100644 --- a/packages/backend/test-federation/compose.override.yaml +++ b/packages/backend/test-federation/compose.override.yaml @@ -22,10 +22,10 @@ services: - internal_network_b volumes: - type: volume - source: node_modules + source: node_modules_dev target: /misskey/node_modules - type: volume - source: node_modules_backend + source: node_modules_backend_dev target: /misskey/packages/backend/node_modules redis.test: diff --git a/packages/backend/test-federation/compose.yml b/packages/backend/test-federation/compose.yml index e98b0fb772ca..5ae4582a052b 100644 --- a/packages/backend/test-federation/compose.yml +++ b/packages/backend/test-federation/compose.yml @@ -76,7 +76,6 @@ services: misskey.b.test: condition: service_healthy environment: - - NODE_ENV=production - TESTER_IP_ADDRESS=$TESTER_IP_ADDRESS volumes: - type: bind @@ -84,8 +83,12 @@ services: target: /misskey/packages/backend/package.json read_only: true - type: bind - source: ./built - target: /misskey/packages/backend/test-federation + source: ./daemon.ts + target: /misskey/packages/backend/test-federation/daemon.ts + read_only: true + - type: bind + source: ./tsconfig.json + target: /misskey/packages/backend/test-federation/tsconfig.json read_only: true - type: bind source: ../../../package.json @@ -104,7 +107,8 @@ services: bash -c " corepack enable && corepack prepare pnpm -F backend i - node ./packages/backend/test-federation/daemon.js + pnpm exec tsc -p ./packages/backend/test-federation + node ./packages/backend/test-federation/built/daemon.js " redis.test: From 4110de548d376d6f53beb21fba5931413be40b4d Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 13:28:04 +0900 Subject: [PATCH 033/111] style: indent --- packages/backend/test-federation/test/drive.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index 589d60084caf..ac6c7dc96ef0 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -63,7 +63,7 @@ describe('Drive', () => { test('Check consistency', () => { // console.log(`a.test: ${JSON.stringify(updatedImage, null, '\t')}`); - // console.log(`b.test: ${JSON.stringify(updatedImageInBServer, null, '\t')}`); + // console.log(`b.test: ${JSON.stringify(updatedImageInBServer, null, '\t')}`); // FIXME: not updated with `drive/files/update` strictEqual(updatedImage.isSensitive, true); From 02e3dd9988191dfcb602412c7ace293446bc0445 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 15:48:07 +0900 Subject: [PATCH 034/111] test(streaming): timeline --- .../test-federation/test/streaming.test.ts | 231 ++++++++++++++++++ .../backend/test-federation/test/utils.ts | 36 +++ 2 files changed, 267 insertions(+) create mode 100644 packages/backend/test-federation/test/streaming.test.ts diff --git a/packages/backend/test-federation/test/streaming.test.ts b/packages/backend/test-federation/test/streaming.test.ts new file mode 100644 index 000000000000..c5fdbcf4c790 --- /dev/null +++ b/packages/backend/test-federation/test/streaming.test.ts @@ -0,0 +1,231 @@ +import { strictEqual } from 'assert'; +import * as Misskey from 'misskey-js'; +import { createAccount, fetchAdmin, isFired, resolveRemoteUser } from './utils.js'; + +const [ + [, aAdminClient], + [, bAdminClient], +] = await Promise.all([ + fetchAdmin('a.test'), + fetchAdmin('b.test'), +]); + +describe('Streaming', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), + resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + ]); + + await bobClient.request('following/create', { userId: aliceInBServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + }); + + describe('Timelines', () => { + async function postFromAlice(params?: Misskey.entities.NotesCreateRequest) { + await aliceClient.request('notes/create', { text: 'a', ...params }); + } + + async function postAndCheckReception( + timeline: C, + expect: boolean, + noteParams: Misskey.entities.NotesCreateRequest = {}, + channelParams: Misskey.Channels[C]['params'] = {}, + ) { + const text = noteParams.text ?? crypto.randomUUID(); + const fired = await isFired( + 'https://b.test', bob, timeline, + async () => await postFromAlice({ text, ...noteParams }), + 'note', msg => msg.text === text, + channelParams, + ); + strictEqual(fired, expect); + } + + describe('homeTimeline', () => { + // NOTE: narrowing scope intentionally to prevent mistakes by copy-and-paste + const homeTimeline = 'homeTimeline'; + + test('Receive remote followee\'s note', async () => { + await postAndCheckReception(homeTimeline, true); + }); + + test('Receive remote followee\'s home-only note', async () => { + await postAndCheckReception(homeTimeline, true, { visibility: 'home' }); + }); + + test('Receive remote followee\'s followers-only note', async () => { + await postAndCheckReception(homeTimeline, true, { visibility: 'followers' }); + }); + + test('Receive remote followee\'s specified-only note', async () => { + await postAndCheckReception(homeTimeline, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + }); + }); + + describe('localTimeline', () => { + const localTimeline = 'localTimeline'; + + test('Don\'t receive remote followee\'s note', async () => { + await postAndCheckReception(localTimeline, false, { visibility: 'followers', visibleUserIds: [bobInAServer.id] }); + }); + }); + + describe('hybridTimeline', () => { + const hybridTimeline = 'hybridTimeline'; + + test('Receive remote followee\'s note', async () => { + await postAndCheckReception(hybridTimeline, true); + }); + + test('Receive remote followee\'s home-only note', async () => { + await postAndCheckReception(hybridTimeline, true, { visibility: 'home' }); + }); + + test('Receive remote followee\'s followers-only note', async () => { + await postAndCheckReception(hybridTimeline, true, { visibility: 'followers' }); + }); + + test('Receive remote followee\'s specified-only note', async () => { + await postAndCheckReception(hybridTimeline, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + }); + }); + + describe('globalTimeline', () => { + const globalTimeline = 'globalTimeline'; + + test('Receive remote followee\'s note', async () => { + await postAndCheckReception(globalTimeline, true); + }); + + test('Don\'t receive remote followee\'s home-only note', async () => { + await postAndCheckReception(globalTimeline, false, { visibility: 'home' }); + }); + + test('Don\'t receive remote followee\'s followers-only note', async () => { + await postAndCheckReception(globalTimeline, false, { visibility: 'followers' }); + }); + + test('Don\'t receive remote followee\'s specified-only note', async () => { + await postAndCheckReception(globalTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + }); + }); + + describe('userList', () => { + const userList = 'userList'; + + let list: Misskey.entities.UserList; + + beforeAll(async () => { + list = await bobClient.request('users/lists/create', { name: 'Bob\'s List' }); + await bobClient.request('users/lists/push', { listId: list.id, userId: aliceInBServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + }); + + test('Receive remote followee\'s note', async () => { + await postAndCheckReception(userList, true, {}, { listId: list.id }); + }); + + test('Receive remote followee\'s home-only note', async () => { + await postAndCheckReception(userList, true, { visibility: 'home' }, { listId: list.id }); + }); + + test('Receive remote followee\'s followers-only note', async () => { + await postAndCheckReception(userList, true, { visibility: 'followers' }, { listId: list.id }); + }); + + test('Receive remote followee\'s specified-only note', async () => { + await postAndCheckReception(userList, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { listId: list.id }); + }); + }); + + describe('roleTimeline', () => { + const roleTimeline = 'roleTimeline'; + + let role: Misskey.entities.Role; + + beforeAll(async () => { + role = await bAdminClient.request('admin/roles/create', { + name: 'Remote Users', + description: 'Remote users are assigned to this role.', + color: null, + iconUrl: null, + target: 'conditional', + condFormula: { + type: 'isRemote' as never, + }, + isPublic: true, + isModerator: false, + isAdministrator: false, + isExplorable: true, + asBadge: false, + canEditMembersByModerator: false, + displayOrder: 0, + policies: {}, + }); + await new Promise(resolve => setTimeout(resolve, 1000)); + }); + + test('Receive remote followee\'s note', async () => { + await postAndCheckReception(roleTimeline, true, {}, { roleId: role.id }); + }); + + test('Don\'t receive remote followee\'s home-only note', async () => { + await postAndCheckReception(roleTimeline, false, { visibility: 'home' }, { roleId: role.id }); + }); + + test('Don\'t receive remote followee\'s followers-only note', async () => { + await postAndCheckReception(roleTimeline, false, { visibility: 'followers' }, { roleId: role.id }); + }); + + test('Don\'t receive remote followee\'s specified-only note', async () => { + await postAndCheckReception(roleTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { roleId: role.id }); + }); + }); + + // TODO: Cannot test + describe.skip('antenna', () => { + const antenna = 'antenna'; + + let bobAntenna: Misskey.entities.Antenna; + + beforeAll(async () => { + bobAntenna = await bobClient.request('antennas/create', { + name: 'Bob\'s Egosurfing Antenna', + src: 'all', + keywords: [['Bob']], + excludeKeywords: [], + users: [], + caseSensitive: false, + localOnly: false, + withReplies: true, + withFile: true, + }); + await new Promise(resolve => setTimeout(resolve, 1000)); + }); + + test('Receive remote followee\'s note', async () => { + await postAndCheckReception(antenna, true, { text: 'I love Bob (1)' }, { antennaId: bobAntenna.id }); + }); + + test('Don\'t receive remote followee\'s home-only note', async () => { + await postAndCheckReception(antenna, false, { text: 'I love Bob (2)', visibility: 'home' }, { antennaId: bobAntenna.id }); + }); + + test('Don\'t receive remote followee\'s followers-only note', async () => { + await postAndCheckReception(antenna, false, { text: 'I love Bob (3)', visibility: 'followers' }, { antennaId: bobAntenna.id }); + }); + + test('Don\'t receive remote followee\'s specified-only note', async () => { + await postAndCheckReception(antenna, false, { text: 'I love Bob (4)', visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { antennaId: bobAntenna.id }); + }); + }); + }); +}); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index d1649eba0c41..d66d1bb46c87 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -3,6 +3,7 @@ import { readFile } from 'fs/promises'; import { dirname, join } from 'path'; import { fileURLToPath } from 'url'; import * as Misskey from 'misskey-js'; +import { WebSocket } from 'ws'; import { SwitchCaseResponseType } from 'misskey-js/api.types.js'; const __filename = fileURLToPath(import.meta.url); @@ -161,3 +162,38 @@ export function deepStrictEqualWithExcludedFields(actual: T, expected: T, exc } deepStrictEqual(_actual, _expected); } + +export async function isFired( + host: string, + user: { i: string }, + channel: C, + trigger: () => Promise, + type: T, + // @ts-expect-error TODO: why getting error here? + cond: (msg: Parameters[0]) => boolean, + params?: Misskey.Channels[C]['params'], +): Promise { + return new Promise(async (resolve, reject) => { + // @ts-expect-error TODO: why? + const stream = new Misskey.Stream(host, { token: user.i }, { WebSocket }); + const connection = stream.useChannel(channel, params); + connection.on(type as any, ((msg: Misskey.Channels[C]['events'][T]) => { + if (cond(msg)) { + stream.close(); + clearTimeout(timer); + resolve(true); + } + }) as any); + + const timer = setTimeout(() => { + stream.close(); + resolve(false); + }, 2000); + + await trigger().catch(err => { + stream.close(); + clearTimeout(timer); + reject(err); + }); + }); +}; From 8bb100ebee1ed756851eeee87c32e1dcd8c941c5 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 15:50:24 +0900 Subject: [PATCH 035/111] chore: rename --- .../test-federation/test/{blocking.test.ts => block.test.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/backend/test-federation/test/{blocking.test.ts => block.test.ts} (100%) diff --git a/packages/backend/test-federation/test/blocking.test.ts b/packages/backend/test-federation/test/block.test.ts similarity index 100% rename from packages/backend/test-federation/test/blocking.test.ts rename to packages/backend/test-federation/test/block.test.ts From 1387a91691db363137243a993f663ca777f2d2a3 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 16:07:05 +0900 Subject: [PATCH 036/111] fix: delete role after test --- packages/backend/test-federation/test/streaming.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/backend/test-federation/test/streaming.test.ts b/packages/backend/test-federation/test/streaming.test.ts index c5fdbcf4c790..8fbbefc4e2a3 100644 --- a/packages/backend/test-federation/test/streaming.test.ts +++ b/packages/backend/test-federation/test/streaming.test.ts @@ -188,6 +188,10 @@ describe('Streaming', () => { test('Don\'t receive remote followee\'s specified-only note', async () => { await postAndCheckReception(roleTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { roleId: role.id }); }); + + afterAll(async () => { + await bAdminClient.request('admin/roles/delete', { roleId: role.id }); + }); }); // TODO: Cannot test From acca8b86ec5a71d8825a1bfa66b4f033288b2f76 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 16:12:51 +0900 Subject: [PATCH 037/111] refactor: resolve users by uri --- .../test-federation/test/block.test.ts | 36 +++++++------- .../backend/test-federation/test/note.test.ts | 12 ++--- .../test-federation/test/streaming.test.ts | 12 ++--- .../backend/test-federation/test/user.test.ts | 48 +++++++++---------- .../backend/test-federation/test/utils.ts | 6 +-- 5 files changed, 57 insertions(+), 57 deletions(-) diff --git a/packages/backend/test-federation/test/block.test.ts b/packages/backend/test-federation/test/block.test.ts index c69762a86d4c..5ee79a65ad32 100644 --- a/packages/backend/test-federation/test/block.test.ts +++ b/packages/backend/test-federation/test/block.test.ts @@ -12,17 +12,17 @@ const [ describe('Blocking', () => { describe('Check follow', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test', aAdminClient); + [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), + resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), ]); }); @@ -93,17 +93,17 @@ describe('Blocking', () => { }); describe('Check reply', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test', aAdminClient); + [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), + resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), ]); }); @@ -135,17 +135,17 @@ describe('Blocking', () => { }); describe('Check reaction', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test', aAdminClient); + [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), + resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), ]); }); diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index ee5ae7bf16cf..eb37b5746db3 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -11,17 +11,17 @@ const [ ]); describe('Note', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test', aAdminClient); + [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), + resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), ]); }); diff --git a/packages/backend/test-federation/test/streaming.test.ts b/packages/backend/test-federation/test/streaming.test.ts index 8fbbefc4e2a3..a37d1ffa2fcf 100644 --- a/packages/backend/test-federation/test/streaming.test.ts +++ b/packages/backend/test-federation/test/streaming.test.ts @@ -11,17 +11,17 @@ const [ ]); describe('Streaming', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test', aAdminClient); + [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), + resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), ]); await bobClient.request('following/create', { userId: aliceInBServer.id }); diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index 3a195d8cc47e..1b07217b0949 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -24,7 +24,7 @@ describe('User', () => { const aliceInAServer = await aliceWatcherClient.request('users/show', { userId: alice.id }); - const resolved = await resolveRemoteUser(`https://a.test/@${aliceInAServer.username}`, aliceWatcherInBServerClient); + const resolved = await resolveRemoteUser(`https://a.test/users/${aliceInAServer.id}`, aliceWatcherInBServerClient); const aliceInBServer = await aliceWatcherInBServerClient.request('users/show', { userId: resolved.id }); @@ -46,17 +46,17 @@ describe('User', () => { }); describe('isCat is federated', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test', aAdminClient); + [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), + resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), ]); }); @@ -77,14 +77,14 @@ describe('User', () => { }); describe('Pinning Notes', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; let aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); - aliceInBServer = await resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient); + [alice, aliceClient] = await createAccount('a.test', aAdminClient); + [bob, bobClient] = await createAccount('b.test', bAdminClient); + aliceInBServer = await resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient); await bobClient.request('following/create', { userId: aliceInBServer.id }); }); @@ -131,17 +131,17 @@ describe('User', () => { }); describe('Follow / Unfollow', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test', aAdminClient); + [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), + resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), ]); }); @@ -193,17 +193,17 @@ describe('User', () => { }); describe('Follow requests', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test', aAdminClient); + [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/@${bobUsername}`, aliceClient), - resolveRemoteUser(`https://a.test/@${aliceUsername}`, bobClient), + resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), + resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), ]); await aliceClient.request('i/update', { isLocked: true }); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index d66d1bb46c87..feae04f253e9 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -106,12 +106,12 @@ export async function createAccount(host: string, adminClient: Misskey.api.APICl ]; } -export async function resolveRemoteUser(url: string, fromClient: Misskey.api.APIClient): Promise { +export async function resolveRemoteUser(uri: string, fromClient: Misskey.api.APIClient): Promise { return new Promise((resolve, reject) => { - fromClient.request('ap/show', { uri: url }) + fromClient.request('ap/show', { uri }) .then(res => { strictEqual(res.type, 'User'); - strictEqual(res.object.url, url); + strictEqual(res.object.uri, uri); resolve(res.object); }) .catch(err => reject(err)); From 1ef73c7bdd0c27f2ac2c3f35e49fb61da74fe429 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 16:15:16 +0900 Subject: [PATCH 038/111] fix: delete antenna after test --- packages/backend/test-federation/test/streaming.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/backend/test-federation/test/streaming.test.ts b/packages/backend/test-federation/test/streaming.test.ts index a37d1ffa2fcf..2fde43e5defb 100644 --- a/packages/backend/test-federation/test/streaming.test.ts +++ b/packages/backend/test-federation/test/streaming.test.ts @@ -230,6 +230,10 @@ describe('Streaming', () => { test('Don\'t receive remote followee\'s specified-only note', async () => { await postAndCheckReception(antenna, false, { text: 'I love Bob (4)', visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { antennaId: bobAntenna.id }); }); + + afterAll(async () => { + await bobClient.request('antennas/delete', { antennaId: bobAntenna.id }); + }); }); }); }); From 21b07619cfb62d254bb15c471c5e8e766565f1e0 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 16:47:55 +0900 Subject: [PATCH 039/111] test: api timeline --- .../test-federation/test/streaming.test.ts | 239 ---------------- .../test-federation/test/timeline.test.ts | 259 ++++++++++++++++++ .../backend/test-federation/test/utils.ts | 2 +- 3 files changed, 260 insertions(+), 240 deletions(-) delete mode 100644 packages/backend/test-federation/test/streaming.test.ts create mode 100644 packages/backend/test-federation/test/timeline.test.ts diff --git a/packages/backend/test-federation/test/streaming.test.ts b/packages/backend/test-federation/test/streaming.test.ts deleted file mode 100644 index 2fde43e5defb..000000000000 --- a/packages/backend/test-federation/test/streaming.test.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { strictEqual } from 'assert'; -import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, isFired, resolveRemoteUser } from './utils.js'; - -const [ - [, aAdminClient], - [, bAdminClient], -] = await Promise.all([ - fetchAdmin('a.test'), - fetchAdmin('b.test'), -]); - -describe('Streaming', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; - - beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test', aAdminClient); - [bob, bobClient] = await createAccount('b.test', bAdminClient); - - [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), - resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), - ]); - - await bobClient.request('following/create', { userId: aliceInBServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); - }); - - describe('Timelines', () => { - async function postFromAlice(params?: Misskey.entities.NotesCreateRequest) { - await aliceClient.request('notes/create', { text: 'a', ...params }); - } - - async function postAndCheckReception( - timeline: C, - expect: boolean, - noteParams: Misskey.entities.NotesCreateRequest = {}, - channelParams: Misskey.Channels[C]['params'] = {}, - ) { - const text = noteParams.text ?? crypto.randomUUID(); - const fired = await isFired( - 'https://b.test', bob, timeline, - async () => await postFromAlice({ text, ...noteParams }), - 'note', msg => msg.text === text, - channelParams, - ); - strictEqual(fired, expect); - } - - describe('homeTimeline', () => { - // NOTE: narrowing scope intentionally to prevent mistakes by copy-and-paste - const homeTimeline = 'homeTimeline'; - - test('Receive remote followee\'s note', async () => { - await postAndCheckReception(homeTimeline, true); - }); - - test('Receive remote followee\'s home-only note', async () => { - await postAndCheckReception(homeTimeline, true, { visibility: 'home' }); - }); - - test('Receive remote followee\'s followers-only note', async () => { - await postAndCheckReception(homeTimeline, true, { visibility: 'followers' }); - }); - - test('Receive remote followee\'s specified-only note', async () => { - await postAndCheckReception(homeTimeline, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); - }); - }); - - describe('localTimeline', () => { - const localTimeline = 'localTimeline'; - - test('Don\'t receive remote followee\'s note', async () => { - await postAndCheckReception(localTimeline, false, { visibility: 'followers', visibleUserIds: [bobInAServer.id] }); - }); - }); - - describe('hybridTimeline', () => { - const hybridTimeline = 'hybridTimeline'; - - test('Receive remote followee\'s note', async () => { - await postAndCheckReception(hybridTimeline, true); - }); - - test('Receive remote followee\'s home-only note', async () => { - await postAndCheckReception(hybridTimeline, true, { visibility: 'home' }); - }); - - test('Receive remote followee\'s followers-only note', async () => { - await postAndCheckReception(hybridTimeline, true, { visibility: 'followers' }); - }); - - test('Receive remote followee\'s specified-only note', async () => { - await postAndCheckReception(hybridTimeline, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); - }); - }); - - describe('globalTimeline', () => { - const globalTimeline = 'globalTimeline'; - - test('Receive remote followee\'s note', async () => { - await postAndCheckReception(globalTimeline, true); - }); - - test('Don\'t receive remote followee\'s home-only note', async () => { - await postAndCheckReception(globalTimeline, false, { visibility: 'home' }); - }); - - test('Don\'t receive remote followee\'s followers-only note', async () => { - await postAndCheckReception(globalTimeline, false, { visibility: 'followers' }); - }); - - test('Don\'t receive remote followee\'s specified-only note', async () => { - await postAndCheckReception(globalTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); - }); - }); - - describe('userList', () => { - const userList = 'userList'; - - let list: Misskey.entities.UserList; - - beforeAll(async () => { - list = await bobClient.request('users/lists/create', { name: 'Bob\'s List' }); - await bobClient.request('users/lists/push', { listId: list.id, userId: aliceInBServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); - }); - - test('Receive remote followee\'s note', async () => { - await postAndCheckReception(userList, true, {}, { listId: list.id }); - }); - - test('Receive remote followee\'s home-only note', async () => { - await postAndCheckReception(userList, true, { visibility: 'home' }, { listId: list.id }); - }); - - test('Receive remote followee\'s followers-only note', async () => { - await postAndCheckReception(userList, true, { visibility: 'followers' }, { listId: list.id }); - }); - - test('Receive remote followee\'s specified-only note', async () => { - await postAndCheckReception(userList, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { listId: list.id }); - }); - }); - - describe('roleTimeline', () => { - const roleTimeline = 'roleTimeline'; - - let role: Misskey.entities.Role; - - beforeAll(async () => { - role = await bAdminClient.request('admin/roles/create', { - name: 'Remote Users', - description: 'Remote users are assigned to this role.', - color: null, - iconUrl: null, - target: 'conditional', - condFormula: { - type: 'isRemote' as never, - }, - isPublic: true, - isModerator: false, - isAdministrator: false, - isExplorable: true, - asBadge: false, - canEditMembersByModerator: false, - displayOrder: 0, - policies: {}, - }); - await new Promise(resolve => setTimeout(resolve, 1000)); - }); - - test('Receive remote followee\'s note', async () => { - await postAndCheckReception(roleTimeline, true, {}, { roleId: role.id }); - }); - - test('Don\'t receive remote followee\'s home-only note', async () => { - await postAndCheckReception(roleTimeline, false, { visibility: 'home' }, { roleId: role.id }); - }); - - test('Don\'t receive remote followee\'s followers-only note', async () => { - await postAndCheckReception(roleTimeline, false, { visibility: 'followers' }, { roleId: role.id }); - }); - - test('Don\'t receive remote followee\'s specified-only note', async () => { - await postAndCheckReception(roleTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { roleId: role.id }); - }); - - afterAll(async () => { - await bAdminClient.request('admin/roles/delete', { roleId: role.id }); - }); - }); - - // TODO: Cannot test - describe.skip('antenna', () => { - const antenna = 'antenna'; - - let bobAntenna: Misskey.entities.Antenna; - - beforeAll(async () => { - bobAntenna = await bobClient.request('antennas/create', { - name: 'Bob\'s Egosurfing Antenna', - src: 'all', - keywords: [['Bob']], - excludeKeywords: [], - users: [], - caseSensitive: false, - localOnly: false, - withReplies: true, - withFile: true, - }); - await new Promise(resolve => setTimeout(resolve, 1000)); - }); - - test('Receive remote followee\'s note', async () => { - await postAndCheckReception(antenna, true, { text: 'I love Bob (1)' }, { antennaId: bobAntenna.id }); - }); - - test('Don\'t receive remote followee\'s home-only note', async () => { - await postAndCheckReception(antenna, false, { text: 'I love Bob (2)', visibility: 'home' }, { antennaId: bobAntenna.id }); - }); - - test('Don\'t receive remote followee\'s followers-only note', async () => { - await postAndCheckReception(antenna, false, { text: 'I love Bob (3)', visibility: 'followers' }, { antennaId: bobAntenna.id }); - }); - - test('Don\'t receive remote followee\'s specified-only note', async () => { - await postAndCheckReception(antenna, false, { text: 'I love Bob (4)', visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { antennaId: bobAntenna.id }); - }); - - afterAll(async () => { - await bobClient.request('antennas/delete', { antennaId: bobAntenna.id }); - }); - }); - }); -}); diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts new file mode 100644 index 000000000000..cc69c9024687 --- /dev/null +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -0,0 +1,259 @@ +import { strictEqual } from 'assert'; +import * as Misskey from 'misskey-js'; +import { createAccount, fetchAdmin, isFired, type Request, resolveRemoteUser } from './utils.js'; + +const [ + [, aAdminClient], + [, bAdminClient], +] = await Promise.all([ + fetchAdmin('a.test'), + fetchAdmin('b.test'), +]); + +describe('Timeline', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, aliceClient] = await createAccount('a.test', aAdminClient); + [bob, bobClient] = await createAccount('b.test', bAdminClient); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), + resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), + ]); + + await bobClient.request('following/create', { userId: aliceInBServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + }); + + async function postFromAlice(params?: Misskey.entities.NotesCreateRequest) { + await aliceClient.request('notes/create', { text: 'a', ...params }); + } + + type TimelineChannel = keyof Misskey.Channels & (`${string}Timeline` | 'antenna' | 'userList'); + type TimelineEndpoint = keyof Misskey.Endpoints & (`${string}timeline` | 'antennas/notes' | 'roles/notes'); + const timelineMap = new Map([ + ['antenna', 'antennas/notes'], + ['globalTimeline', 'notes/global-timeline'], + ['homeTimeline', 'notes/timeline'], + ['hybridTimeline', 'notes/hybrid-timeline'], + ['localTimeline', 'notes/local-timeline'], + ['roleTimeline', 'roles/notes'], + ['userList', 'notes/user-list-timeline'], + ]); + + async function postAndCheckReception( + timelineChannel: C, + expect: boolean, + noteParams: Misskey.entities.NotesCreateRequest = {}, + channelParams: Misskey.Channels[C]['params'] = {}, + ) { + const text = noteParams.text ?? crypto.randomUUID(); + const streamingFired = await isFired( + 'https://b.test', bob, timelineChannel, + async () => await postFromAlice({ text, ...noteParams }), + 'note', msg => msg.text === text, + channelParams, + ); + strictEqual(streamingFired, expect); + + const endpoint = timelineMap.get(timelineChannel)!; + const params: Misskey.Endpoints[typeof endpoint]['req'] = + endpoint === 'antennas/notes' ? { antennaId: (channelParams as Misskey.Channels['antenna']['params']).antennaId } : + endpoint === 'notes/user-list-timeline' ? { listId: (channelParams as Misskey.Channels['userList']['params']).listId } : + endpoint === 'roles/notes' ? { roleId: (channelParams as Misskey.Channels['roleTimeline']['params']).roleId } : + {}; + const notes = await (bobClient.request as Request)(endpoint, params); + const endpointFired = notes.some(note => note.text === text); + strictEqual(endpointFired, expect); + } + + describe('homeTimeline', () => { + // NOTE: narrowing scope intentionally to prevent mistakes by copy-and-paste + const homeTimeline = 'homeTimeline'; + + test('Receive remote followee\'s note', async () => { + await postAndCheckReception(homeTimeline, true); + }); + + test('Receive remote followee\'s home-only note', async () => { + await postAndCheckReception(homeTimeline, true, { visibility: 'home' }); + }); + + test('Receive remote followee\'s followers-only note', async () => { + await postAndCheckReception(homeTimeline, true, { visibility: 'followers' }); + }); + + test('Receive remote followee\'s specified-only note', async () => { + await postAndCheckReception(homeTimeline, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + }); + }); + + describe('localTimeline', () => { + const localTimeline = 'localTimeline'; + + test('Don\'t receive remote followee\'s note', async () => { + await postAndCheckReception(localTimeline, false, { visibility: 'followers', visibleUserIds: [bobInAServer.id] }); + }); + }); + + describe('hybridTimeline', () => { + const hybridTimeline = 'hybridTimeline'; + + test('Receive remote followee\'s note', async () => { + await postAndCheckReception(hybridTimeline, true); + }); + + test('Receive remote followee\'s home-only note', async () => { + await postAndCheckReception(hybridTimeline, true, { visibility: 'home' }); + }); + + test('Receive remote followee\'s followers-only note', async () => { + await postAndCheckReception(hybridTimeline, true, { visibility: 'followers' }); + }); + + test('Receive remote followee\'s specified-only note', async () => { + await postAndCheckReception(hybridTimeline, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + }); + }); + + describe('globalTimeline', () => { + const globalTimeline = 'globalTimeline'; + + test('Receive remote followee\'s note', async () => { + await postAndCheckReception(globalTimeline, true); + }); + + test('Don\'t receive remote followee\'s home-only note', async () => { + await postAndCheckReception(globalTimeline, false, { visibility: 'home' }); + }); + + test('Don\'t receive remote followee\'s followers-only note', async () => { + await postAndCheckReception(globalTimeline, false, { visibility: 'followers' }); + }); + + test('Don\'t receive remote followee\'s specified-only note', async () => { + await postAndCheckReception(globalTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + }); + }); + + describe('userList', () => { + const userList = 'userList'; + + let list: Misskey.entities.UserList; + + beforeAll(async () => { + list = await bobClient.request('users/lists/create', { name: 'Bob\'s List' }); + await bobClient.request('users/lists/push', { listId: list.id, userId: aliceInBServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + }); + + test('Receive remote followee\'s note', async () => { + await postAndCheckReception(userList, true, {}, { listId: list.id }); + }); + + test('Receive remote followee\'s home-only note', async () => { + await postAndCheckReception(userList, true, { visibility: 'home' }, { listId: list.id }); + }); + + test('Receive remote followee\'s followers-only note', async () => { + await postAndCheckReception(userList, true, { visibility: 'followers' }, { listId: list.id }); + }); + + test('Receive remote followee\'s specified-only note', async () => { + await postAndCheckReception(userList, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { listId: list.id }); + }); + }); + + describe('roleTimeline', () => { + const roleTimeline = 'roleTimeline'; + + let role: Misskey.entities.Role; + + beforeAll(async () => { + role = await bAdminClient.request('admin/roles/create', { + name: 'Remote Users', + description: 'Remote users are assigned to this role.', + color: null, + iconUrl: null, + target: 'conditional', + condFormula: { + type: 'isRemote' as never, + }, + isPublic: true, + isModerator: false, + isAdministrator: false, + isExplorable: true, + asBadge: false, + canEditMembersByModerator: false, + displayOrder: 0, + policies: {}, + }); + await new Promise(resolve => setTimeout(resolve, 1000)); + }); + + test('Receive remote followee\'s note', async () => { + await postAndCheckReception(roleTimeline, true, {}, { roleId: role.id }); + }); + + test('Don\'t receive remote followee\'s home-only note', async () => { + await postAndCheckReception(roleTimeline, false, { visibility: 'home' }, { roleId: role.id }); + }); + + test('Don\'t receive remote followee\'s followers-only note', async () => { + await postAndCheckReception(roleTimeline, false, { visibility: 'followers' }, { roleId: role.id }); + }); + + test('Don\'t receive remote followee\'s specified-only note', async () => { + await postAndCheckReception(roleTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { roleId: role.id }); + }); + + afterAll(async () => { + await bAdminClient.request('admin/roles/delete', { roleId: role.id }); + }); + }); + + // TODO: Cannot test + describe.skip('antenna', () => { + const antenna = 'antenna'; + + let bobAntenna: Misskey.entities.Antenna; + + beforeAll(async () => { + bobAntenna = await bobClient.request('antennas/create', { + name: 'Bob\'s Egosurfing Antenna', + src: 'all', + keywords: [['Bob']], + excludeKeywords: [], + users: [], + caseSensitive: false, + localOnly: false, + withReplies: true, + withFile: true, + }); + await new Promise(resolve => setTimeout(resolve, 1000)); + }); + + test('Receive remote followee\'s note', async () => { + await postAndCheckReception(antenna, true, { text: 'I love Bob (1)' }, { antennaId: bobAntenna.id }); + }); + + test('Don\'t receive remote followee\'s home-only note', async () => { + await postAndCheckReception(antenna, false, { text: 'I love Bob (2)', visibility: 'home' }, { antennaId: bobAntenna.id }); + }); + + test('Don\'t receive remote followee\'s followers-only note', async () => { + await postAndCheckReception(antenna, false, { text: 'I love Bob (3)', visibility: 'followers' }, { antennaId: bobAntenna.id }); + }); + + test('Don\'t receive remote followee\'s specified-only note', async () => { + await postAndCheckReception(antenna, false, { text: 'I love Bob (4)', visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { antennaId: bobAntenna.id }); + }); + + afterAll(async () => { + await bobClient.request('antennas/delete', { antennaId: bobAntenna.id }); + }); + }); +}); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index feae04f253e9..cf9bb6faf77c 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -10,7 +10,7 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); /** used for avoiding overload and some endpoints */ -type Request = ( +export type Request = ( endpoint: E, params: P, credential?: string | null ) => Promise>; From 407ec36bf402d4a6087a09dff005eda21538e4bb Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 17:14:04 +0900 Subject: [PATCH 040/111] test: Note deletion --- .../backend/test-federation/test/note.test.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index eb37b5746db3..0bbae16d6ce9 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -148,6 +148,32 @@ describe('Note', () => { }); }); + describe('Deletion', () => { + let carolClient: Misskey.api.APIClient; + + beforeAll(async () => { + [, carolClient] = await createAccount('a.test', aAdminClient); + + await carolClient.request('following/create', { userId: bobInAServer.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + }); + + test('Delete is derivered to followers', async () => { + const note = (await bobClient.request('notes/create', { text: 'I\'m Bob.' })).createdNote; + const noteInAServer = await resolveRemoteNote(`https://b.test/notes/${note.id}`, carolClient); + await bobClient.request('notes/delete', { noteId: note.id }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + await rejects( + async () => await carolClient.request('notes/show', { noteId: noteInAServer.id }), + (err: any) => { + strictEqual(err.code, 'NO_SUCH_NOTE'); + return true; + }, + ); + }); + }); + describe('Reaction', () => { test('Consistency of reaction', async () => { const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; From 41ffbd10d9689944ab13d59dac4dfd285c2dcf92 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 17:37:23 +0900 Subject: [PATCH 041/111] refactor: sleep function --- .../test-federation/test/block.test.ts | 22 +++++++------- .../backend/test-federation/test/move.test.ts | 4 +-- .../backend/test-federation/test/note.test.ts | 10 +++---- .../test-federation/test/timeline.test.ts | 10 +++---- .../backend/test-federation/test/user.test.ts | 30 +++++++++---------- .../backend/test-federation/test/utils.ts | 10 +++++-- 6 files changed, 45 insertions(+), 41 deletions(-) diff --git a/packages/backend/test-federation/test/block.test.ts b/packages/backend/test-federation/test/block.test.ts index 5ee79a65ad32..476a8372c260 100644 --- a/packages/backend/test-federation/test/block.test.ts +++ b/packages/backend/test-federation/test/block.test.ts @@ -1,6 +1,6 @@ import { deepStrictEqual, rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, resolveRemoteNote, resolveRemoteUser } from './utils.js'; +import { createAccount, fetchAdmin, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; const [ [, aAdminClient], @@ -28,7 +28,7 @@ describe('Blocking', () => { test('Cannot follow if blocked', async () => { await aliceClient.request('blocking/create', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); await rejects( async () => await bobClient.request('following/create', { userId: aliceInBServer.id }), (err: any) => { @@ -47,7 +47,7 @@ describe('Blocking', () => { test('Cannot follow even if unblocked', async () => { // unblock here await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); // TODO: why still being blocked? await rejects( @@ -61,10 +61,10 @@ describe('Blocking', () => { test.skip('Can follow if unblocked', async () => { await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); await bobClient.request('following/create', { userId: aliceInBServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); const following = await bobClient.request('users/following', { userId: bob.id }); strictEqual(following.length, 1); @@ -81,7 +81,7 @@ describe('Blocking', () => { }); await aliceClient.request('blocking/create', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); test('after block', async () => { const following = await bobClient.request('users/following', { userId: bob.id }); @@ -109,7 +109,7 @@ describe('Blocking', () => { test('Cannot reply if blocked', async () => { await aliceClient.request('blocking/create', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); @@ -124,7 +124,7 @@ describe('Blocking', () => { test('Can reply if unblocked', async () => { await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); @@ -151,7 +151,7 @@ describe('Blocking', () => { test('Cannot reply if blocked', async () => { await aliceClient.request('blocking/create', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); @@ -168,7 +168,7 @@ describe('Blocking', () => { test('Cannot reply even if unblocked', async () => { // unblock here await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); @@ -185,7 +185,7 @@ describe('Blocking', () => { test.skip('Can reply if unblocked', async () => { await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); diff --git a/packages/backend/test-federation/test/move.test.ts b/packages/backend/test-federation/test/move.test.ts index 9e0201ded3b0..74c316c2dd79 100644 --- a/packages/backend/test-federation/test/move.test.ts +++ b/packages/backend/test-federation/test/move.test.ts @@ -1,6 +1,6 @@ import assert, { strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin } from './utils.js'; +import { createAccount, fetchAdmin, sleep } from './utils.js'; const [ [, aAdminClient], @@ -36,7 +36,7 @@ describe('Move', () => { // Move @alice@a.test ==> @bob@b.test await bobClient.request('i/update', { alsoKnownAs: [`@${aliceUsername}@a.test`] }); await aliceClient.request('i/move', { moveToAccount: `@${bobUsername}@b.test` }); - await new Promise(resolve => setTimeout(resolve, 3000)); + await sleep(3000); }); test('Check from follower', async () => { diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index 0bbae16d6ce9..b4f401903137 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -1,6 +1,6 @@ import assert, { rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, resolveRemoteUser, uploadFile } from './utils.js'; +import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, resolveRemoteUser, sleep, uploadFile } from './utils.js'; const [ [, aAdminClient], @@ -91,7 +91,7 @@ describe('Note', () => { ]); strictEqual(aliceInBServer.id, resolvedNote.userId); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); const resolvedReplyedNote = await bobClient.request('notes/show', { noteId: resolvedNote.replyId }); strictEqual(resolvedReplyedNote.repliesCount, 1); @@ -155,14 +155,14 @@ describe('Note', () => { [, carolClient] = await createAccount('a.test', aAdminClient); await carolClient.request('following/create', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); }); test('Delete is derivered to followers', async () => { const note = (await bobClient.request('notes/create', { text: 'I\'m Bob.' })).createdNote; const noteInAServer = await resolveRemoteNote(`https://b.test/notes/${note.id}`, carolClient); await bobClient.request('notes/delete', { noteId: note.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); await rejects( async () => await carolClient.request('notes/show', { noteId: noteInAServer.id }), @@ -180,7 +180,7 @@ describe('Note', () => { const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); const reaction = '😅'; await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); const reactions = await aliceClient.request('notes/reactions', { noteId: note.id }); strictEqual(reactions.length, 1); diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index cc69c9024687..e136877395fb 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -1,6 +1,6 @@ import { strictEqual } from 'assert'; import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, isFired, type Request, resolveRemoteUser } from './utils.js'; +import { createAccount, fetchAdmin, isFired, type Request, resolveRemoteUser, sleep } from './utils.js'; const [ [, aAdminClient], @@ -25,7 +25,7 @@ describe('Timeline', () => { ]); await bobClient.request('following/create', { userId: aliceInBServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); }); async function postFromAlice(params?: Misskey.entities.NotesCreateRequest) { @@ -147,7 +147,7 @@ describe('Timeline', () => { beforeAll(async () => { list = await bobClient.request('users/lists/create', { name: 'Bob\'s List' }); await bobClient.request('users/lists/push', { listId: list.id, userId: aliceInBServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); }); test('Receive remote followee\'s note', async () => { @@ -191,7 +191,7 @@ describe('Timeline', () => { displayOrder: 0, policies: {}, }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); }); test('Receive remote followee\'s note', async () => { @@ -233,7 +233,7 @@ describe('Timeline', () => { withReplies: true, withFile: true, }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); }); test('Receive remote followee\'s note', async () => { diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index 1b07217b0949..f77d56cf503b 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -1,6 +1,6 @@ import { rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, resolveRemoteUser } from './utils.js'; +import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; const [ [, aAdminClient], @@ -66,10 +66,10 @@ describe('User', () => { test('Becoming a cat is sent to their followers', async () => { await bobClient.request('following/create', { userId: aliceInBServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); await aliceClient.request('i/update', { isCat: true }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); const res = await bobClient.request('users/show', { userId: aliceInBServer.id }); strictEqual(res.isCat, true); @@ -92,7 +92,7 @@ describe('User', () => { test('Pinning localOnly Note is not delivered', async () => { const note = (await aliceClient.request('notes/create', { text: 'a', localOnly: true })).createdNote; await aliceClient.request('i/pin', { noteId: note.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); @@ -101,7 +101,7 @@ describe('User', () => { test('Pinning followers-only Note is not delivered', async () => { const note = (await aliceClient.request('notes/create', { text: 'a', visibility: 'followers' })).createdNote; await aliceClient.request('i/pin', { noteId: note.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); @@ -112,7 +112,7 @@ describe('User', () => { test('Pinning normal Note is delivered', async () => { pinnedNote = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; await aliceClient.request('i/pin', { noteId: pinnedNote.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 1); @@ -122,7 +122,7 @@ describe('User', () => { test('Unpinning normal Note is delivered', async () => { await aliceClient.request('i/unpin', { noteId: pinnedNote.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); @@ -149,7 +149,7 @@ describe('User', () => { beforeAll(async () => { await aliceClient.request('following/create', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); }); test('Check consistency with `users/following` and `users/followers` endpoints', async () => { @@ -172,7 +172,7 @@ describe('User', () => { beforeAll(async () => { await aliceClient.request('following/delete', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); }); test('Check consistency with `users/following` and `users/followers` endpoints', async () => { @@ -213,7 +213,7 @@ describe('User', () => { describe('Bob sends follow request to Alice', () => { beforeAll(async () => { await bobClient.request('following/create', { userId: aliceInBServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); }); test('Alice should have a request', async () => { @@ -227,7 +227,7 @@ describe('User', () => { describe('Alice cancels it', () => { beforeAll(async () => { await bobClient.request('following/requests/cancel', { userId: aliceInBServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); }); test('Alice should have no requests', async () => { @@ -240,10 +240,10 @@ describe('User', () => { describe('Send follow request from Bob to Alice and reject', () => { beforeAll(async () => { await bobClient.request('following/create', { userId: aliceInBServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); await aliceClient.request('following/requests/reject', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); }); test('Bob should have no requests', async () => { @@ -265,10 +265,10 @@ describe('User', () => { describe('Send follow request from Bob to Alice and accept', () => { beforeAll(async () => { await bobClient.request('following/create', { userId: aliceInBServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); await aliceClient.request('following/requests/accept', { userId: bobInAServer.id }); - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); }); test('Bob follows Alice', async () => { diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index cf9bb6faf77c..f0a08dd76e95 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -16,9 +16,13 @@ export type Request = { + return new Promise(resolve => setTimeout(resolve, ms)); +} + export async function signin(host: string, params: Misskey.entities.SigninRequest): Promise { // wait for a second to prevent hit rate limit - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); // console.log(`Sign in to @${params.username}@${host} ...`); return await (new Misskey.api.APIClient({ origin: `https://${host}`, @@ -36,7 +40,7 @@ export async function signin(host: string, params: Misskey.entities.SigninReques }) .catch(async err => { if (err.id === '22d05606-fbcf-421a-a2db-b32610dcfd1b') { - await new Promise(resolve => setTimeout(resolve, Math.random() * 5000)); + await sleep(Math.random() * 5000); return await signin(host, params); } throw err; @@ -72,7 +76,7 @@ async function createAdmin(host: string): Promise { - await new Promise(resolve => setTimeout(resolve, Math.random() * 5000)); + await sleep(Math.random() * 5000); const admin = adminCache.get(host) ?? await signin(host, ADMIN_PARAMS) .then(res => { From 228606444c4b8d043d1d8680dc98f9b2d72241bf Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 18:37:44 +0900 Subject: [PATCH 042/111] test: notification --- .../test-federation/test/notification.test.ts | 59 +++++++++++++++++++ .../backend/test-federation/test/utils.ts | 2 +- 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 packages/backend/test-federation/test/notification.test.ts diff --git a/packages/backend/test-federation/test/notification.test.ts b/packages/backend/test-federation/test/notification.test.ts new file mode 100644 index 000000000000..12d6c2ce5315 --- /dev/null +++ b/packages/backend/test-federation/test/notification.test.ts @@ -0,0 +1,59 @@ +import { deepStrictEqual, strictEqual } from 'assert'; +import * as Misskey from 'misskey-js'; +import { createAccount, fetchAdmin, isFired, resolveRemoteUser } from './utils.js'; + +const [ + [, aAdminClient], + [, bAdminClient], +] = await Promise.all([ + fetchAdmin('a.test'), + fetchAdmin('b.test'), +]); + +describe('Notification', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, aliceClient] = await createAccount('a.test', aAdminClient); + [bob, bobClient] = await createAccount('b.test', bAdminClient); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), + resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), + ]); + }); + + describe('Follow', () => { + test('Get notification when follow/followed', async () => { + const fired = await Promise.all([ + isFired( + 'https://b.test', bob, 'main', + async () => await bobClient.request('following/create', { userId: aliceInBServer.id }), + 'follow', msg => msg.id === aliceInBServer.id, + ), + isFired( + 'https://a.test', alice, 'main', + async () => {}, // NOTE: do nothing because done in above + 'followed', msg => msg.id === bobInAServer.id, + ), + ]); + deepStrictEqual(fired, [true, true]); + + { + const notifications = await bobClient.request('i/notifications', {}); + const notification = notifications[0]; + strictEqual(notification.type, 'followRequestAccepted'); + strictEqual(notification.userId, aliceInBServer.id); + } + + { + const notifications = await aliceClient.request('i/notifications', {}); + const notification = notifications[0]; + strictEqual(notification.type, 'follow'); + strictEqual(notification.userId, bobInAServer.id); + } + }); + }); +}); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index f0a08dd76e95..e492cf74bd96 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -171,7 +171,7 @@ export async function isFired Promise, + trigger: () => Promise, type: T, // @ts-expect-error TODO: why getting error here? cond: (msg: Parameters[0]) => boolean, From 914fb6c13398b72080644559789103ec67a32106 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 18:38:43 +0900 Subject: [PATCH 043/111] style: indent --- packages/backend/test-federation/test/drive.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index ac6c7dc96ef0..58a07c91228b 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -32,8 +32,8 @@ describe('Drive', () => { }); test('Check consistency of DriveFile', () => { - // console.log(`a.test: ${JSON.stringify(image, null, '\t')}`); - // console.log(`b.test: ${JSON.stringify(imageInBServer, null, '\t')}`); + // console.log(`a.test: ${JSON.stringify(image, null, '\t')}`); + // console.log(`b.test: ${JSON.stringify(imageInBServer, null, '\t')}`); deepStrictEqualWithExcludedFields(image, imageInBServer, [ 'id', From 2587458104466384d511419636d00e27d28c1af9 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 18:44:52 +0900 Subject: [PATCH 044/111] refactor: type-safe host --- .../test-federation/test/drive.test.ts | 6 ++--- .../test-federation/test/notification.test.ts | 4 ++-- .../test-federation/test/timeline.test.ts | 2 +- .../backend/test-federation/test/utils.ts | 22 ++++++++++--------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index 58a07c91228b..03be3868a06d 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -24,8 +24,7 @@ describe('Drive', () => { beforeAll(async () => { image = await uploadFile('a.test', '../../test/resources/192.jpg', uploader.i); const noteWithImage = (await uploaderClient.request('notes/create', { fileIds: [image.id] })).createdNote; - const uri = `https://a.test/notes/${noteWithImage.id}`; - const noteInBServer = await resolveRemoteNote(uri, bAdminClient); + const noteInBServer = await resolveRemoteNote(`https://a.test/notes/${noteWithImage.id}`, bAdminClient); assert(noteInBServer.files != null); strictEqual(noteInBServer.files.length, 1); imageInBServer = noteInBServer.files[0]; @@ -78,8 +77,7 @@ describe('Drive', () => { describe('Re-update with attaching to Note', () => { beforeAll(async () => { const noteWithUpdatedImage = (await uploaderClient.request('notes/create', { fileIds: [updatedImage.id] })).createdNote; - const uriUpdated = `https://a.test/notes/${noteWithUpdatedImage.id}`; - const noteWithUpdatedImageInBServer = await resolveRemoteNote(uriUpdated, bAdminClient); + const noteWithUpdatedImageInBServer = await resolveRemoteNote(`https://a.test/notes/${noteWithUpdatedImage.id}`, bAdminClient); assert(noteWithUpdatedImageInBServer.files != null); strictEqual(noteWithUpdatedImageInBServer.files.length, 1); reupdatedImageInBServer = noteWithUpdatedImageInBServer.files[0]; diff --git a/packages/backend/test-federation/test/notification.test.ts b/packages/backend/test-federation/test/notification.test.ts index 12d6c2ce5315..ea58020214b5 100644 --- a/packages/backend/test-federation/test/notification.test.ts +++ b/packages/backend/test-federation/test/notification.test.ts @@ -29,12 +29,12 @@ describe('Notification', () => { test('Get notification when follow/followed', async () => { const fired = await Promise.all([ isFired( - 'https://b.test', bob, 'main', + 'b.test', bob, 'main', async () => await bobClient.request('following/create', { userId: aliceInBServer.id }), 'follow', msg => msg.id === aliceInBServer.id, ), isFired( - 'https://a.test', alice, 'main', + 'a.test', alice, 'main', async () => {}, // NOTE: do nothing because done in above 'followed', msg => msg.id === bobInAServer.id, ), diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index e136877395fb..0231d6af0d01 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -52,7 +52,7 @@ describe('Timeline', () => { ) { const text = noteParams.text ?? crypto.randomUUID(); const streamingFired = await isFired( - 'https://b.test', bob, timelineChannel, + 'b.test', bob, timelineChannel, async () => await postFromAlice({ text, ...noteParams }), 'note', msg => msg.text === text, channelParams, diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index e492cf74bd96..88e67fa5782b 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -14,13 +14,15 @@ export type Request = Promise>; +type Host = 'a.test' | 'b.test'; + export const ADMIN_PARAMS = { username: 'admin', password: 'admin' }; export async function sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } -export async function signin(host: string, params: Misskey.entities.SigninRequest): Promise { +export async function signin(host: Host, params: Misskey.entities.SigninRequest): Promise { // wait for a second to prevent hit rate limit await sleep(1000); // console.log(`Sign in to @${params.username}@${host} ...`); @@ -47,9 +49,9 @@ export async function signin(host: string, params: Misskey.entities.SigninReques }); } -const adminCache = new Map(); +const adminCache = new Map(); -async function createAdmin(host: string): Promise { +async function createAdmin(host: Host): Promise { const client = new Misskey.api.APIClient({ origin: `https://${host}` }); return await client.request('admin/accounts/create', ADMIN_PARAMS).then(res => { console.log(`Successfully created admin account: @${ADMIN_PARAMS.username}@${host}`); @@ -75,7 +77,7 @@ async function createAdmin(host: string): Promise { +export async function fetchAdmin(host: Host): Promise<[Misskey.entities.SigninResponse, Misskey.api.APIClient]> { await sleep(Math.random() * 5000); const admin = adminCache.get(host) ?? await signin(host, ADMIN_PARAMS) @@ -96,7 +98,7 @@ export async function fetchAdmin(host: string): Promise<[Misskey.entities.Signin return [admin, new Misskey.api.APIClient({ origin: `https://${host}`, credential: admin.i })]; } -export async function createAccount(host: string, adminClient: Misskey.api.APIClient): Promise<[Misskey.entities.SigninResponse, Misskey.api.APIClient, { username: string; password: string }]> { +export async function createAccount(host: Host, adminClient: Misskey.api.APIClient): Promise<[Misskey.entities.SigninResponse, Misskey.api.APIClient, { username: string; password: string }]> { const username = crypto.randomUUID().replaceAll('-', '').substring(0, 20); const password = crypto.randomUUID().replaceAll('-', ''); await adminClient.request('admin/accounts/create', { username, password }); @@ -110,7 +112,7 @@ export async function createAccount(host: string, adminClient: Misskey.api.APICl ]; } -export async function resolveRemoteUser(uri: string, fromClient: Misskey.api.APIClient): Promise { +export async function resolveRemoteUser(uri: `https://${Host}/users/${string}`, fromClient: Misskey.api.APIClient): Promise { return new Promise((resolve, reject) => { fromClient.request('ap/show', { uri }) .then(res => { @@ -122,7 +124,7 @@ export async function resolveRemoteUser(uri: string, fromClient: Misskey.api.API }); } -export async function resolveRemoteNote(uri: string, fromClient: Misskey.api.APIClient): Promise { +export async function resolveRemoteNote(uri: `https://${Host}/notes/${string}`, fromClient: Misskey.api.APIClient): Promise { return new Promise((resolve, reject) => { fromClient.request('ap/show', { uri }) .then(res => { @@ -134,7 +136,7 @@ export async function resolveRemoteNote(uri: string, fromClient: Misskey.api.API }); } -export async function uploadFile(host: string, path: string, token: string): Promise { +export async function uploadFile(host: Host, path: string, token: string): Promise { const filename = path.split('/').pop() ?? 'untitled'; const blob = new Blob([await readFile(join(__dirname, path))]); @@ -168,7 +170,7 @@ export function deepStrictEqualWithExcludedFields(actual: T, expected: T, exc } export async function isFired( - host: string, + host: Host, user: { i: string }, channel: C, trigger: () => Promise, @@ -179,7 +181,7 @@ export async function isFired { return new Promise(async (resolve, reject) => { // @ts-expect-error TODO: why? - const stream = new Misskey.Stream(host, { token: user.i }, { WebSocket }); + const stream = new Misskey.Stream(`wss://${host}`, { token: user.i }, { WebSocket }); const connection = stream.useChannel(channel, params); connection.on(type as any, ((msg: Misskey.Channels[C]['events'][T]) => { if (cond(msg)) { From bb8d9d861495ad3c21052cbf94b33b69d0054c0e Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 19:05:34 +0900 Subject: [PATCH 045/111] docs: update description --- packages/backend/test-federation/README.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/backend/test-federation/README.md b/packages/backend/test-federation/README.md index 38b44041c804..9bc1e87b2561 100644 --- a/packages/backend/test-federation/README.md +++ b/packages/backend/test-federation/README.md @@ -1,12 +1,19 @@ -Execute following commands: +## test-federation +Test federation between two Misskey servers: `a.test` and `b.test`. + +First, you need to start server by executing following commands: ```sh cp ./.env.example ./.env bash ./generate_certificates.sh -pnpm build:fed -docker compose up +docker compose up --scale tester=0 +``` + +Then you can run all tests by a following command: +```sh +docker compose run --rm tester ``` -For testing a specific file, run following: +For testing a specific file, run a following command: ```sh docker compose run --rm tester -- pnpm -F backend test:fed packages/backend/test-federation/test/user.test.ts ``` From 59caa5dfd75ee987719c31c772af9872c5fb30ef Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 21:32:47 +0900 Subject: [PATCH 046/111] refactor: resolve function params --- .../test-federation/test/block.test.ts | 24 +++++++++---------- .../test-federation/test/drive.test.ts | 4 ++-- .../backend/test-federation/test/note.test.ts | 14 +++++------ .../test-federation/test/notification.test.ts | 4 ++-- .../test-federation/test/timeline.test.ts | 4 ++-- .../backend/test-federation/test/user.test.ts | 18 +++++++------- .../backend/test-federation/test/utils.ts | 6 +++-- 7 files changed, 38 insertions(+), 36 deletions(-) diff --git a/packages/backend/test-federation/test/block.test.ts b/packages/backend/test-federation/test/block.test.ts index 476a8372c260..9e0080f39056 100644 --- a/packages/backend/test-federation/test/block.test.ts +++ b/packages/backend/test-federation/test/block.test.ts @@ -21,8 +21,8 @@ describe('Blocking', () => { [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), - resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), + resolveRemoteUser('b.test', bob.id, aliceClient), + resolveRemoteUser('a.test', alice.id, bobClient), ]); }); @@ -102,8 +102,8 @@ describe('Blocking', () => { [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), - resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), + resolveRemoteUser('b.test', bob.id, aliceClient), + resolveRemoteUser('a.test', alice.id, bobClient), ]); }); @@ -112,7 +112,7 @@ describe('Blocking', () => { await sleep(1000); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); await rejects( async () => await bobClient.request('notes/create', { text: 'b', replyId: resolvedNote.id }), (err: any) => { @@ -127,10 +127,10 @@ describe('Blocking', () => { await sleep(1000); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); const reply = (await bobClient.request('notes/create', { text: 'b', replyId: resolvedNote.id })).createdNote; - await resolveRemoteNote(`https://b.test/notes/${reply.id}`, aliceClient); + await resolveRemoteNote('b.test', reply.id, aliceClient); }); }); @@ -144,8 +144,8 @@ describe('Blocking', () => { [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), - resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), + resolveRemoteUser('b.test', bob.id, aliceClient), + resolveRemoteUser('a.test', alice.id, bobClient), ]); }); @@ -154,7 +154,7 @@ describe('Blocking', () => { await sleep(1000); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); await rejects( async () => await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: '😅' }), (err: any) => { @@ -171,7 +171,7 @@ describe('Blocking', () => { await sleep(1000); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); // TODO: why still being blocked? await rejects( @@ -188,7 +188,7 @@ describe('Blocking', () => { await sleep(1000); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: '😅' }); const _note = await aliceClient.request('notes/show', { noteId: note.id }); diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index 03be3868a06d..170e0f83fad1 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -24,7 +24,7 @@ describe('Drive', () => { beforeAll(async () => { image = await uploadFile('a.test', '../../test/resources/192.jpg', uploader.i); const noteWithImage = (await uploaderClient.request('notes/create', { fileIds: [image.id] })).createdNote; - const noteInBServer = await resolveRemoteNote(`https://a.test/notes/${noteWithImage.id}`, bAdminClient); + const noteInBServer = await resolveRemoteNote('a.test', noteWithImage.id, bAdminClient); assert(noteInBServer.files != null); strictEqual(noteInBServer.files.length, 1); imageInBServer = noteInBServer.files[0]; @@ -77,7 +77,7 @@ describe('Drive', () => { describe('Re-update with attaching to Note', () => { beforeAll(async () => { const noteWithUpdatedImage = (await uploaderClient.request('notes/create', { fileIds: [updatedImage.id] })).createdNote; - const noteWithUpdatedImageInBServer = await resolveRemoteNote(`https://a.test/notes/${noteWithUpdatedImage.id}`, bAdminClient); + const noteWithUpdatedImageInBServer = await resolveRemoteNote('a.test', noteWithUpdatedImage.id, bAdminClient); assert(noteWithUpdatedImageInBServer.files != null); strictEqual(noteWithUpdatedImageInBServer.files.length, 1); reupdatedImageInBServer = noteWithUpdatedImageInBServer.files[0]; diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index b4f401903137..96168629b61b 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -20,8 +20,8 @@ describe('Note', () => { [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), - resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), + resolveRemoteUser('b.test', bob.id, aliceClient), + resolveRemoteUser('a.test', alice.id, bobClient), ]); }); @@ -38,7 +38,7 @@ describe('Note', () => { }, })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); deepStrictEqualWithExcludedFields(note, resolvedNote, [ 'id', 'emojis', @@ -65,7 +65,7 @@ describe('Note', () => { const replyedNote = await aliceClient.request('notes/show', { noteId: _replyedNote.id }); strictEqual(replyedNote.repliesCount, 1); - const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); deepStrictEqualWithExcludedFields(note, resolvedNote, [ 'id', 'emojis', @@ -107,7 +107,7 @@ describe('Note', () => { renoteId: renotedNote.id, })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); deepStrictEqualWithExcludedFields(note, resolvedNote, [ 'id', 'emojis', @@ -160,7 +160,7 @@ describe('Note', () => { test('Delete is derivered to followers', async () => { const note = (await bobClient.request('notes/create', { text: 'I\'m Bob.' })).createdNote; - const noteInAServer = await resolveRemoteNote(`https://b.test/notes/${note.id}`, carolClient); + const noteInAServer = await resolveRemoteNote('b.test', note.id, carolClient); await bobClient.request('notes/delete', { noteId: note.id }); await sleep(1000); @@ -177,7 +177,7 @@ describe('Note', () => { describe('Reaction', () => { test('Consistency of reaction', async () => { const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote(`https://a.test/notes/${note.id}`, bobClient); + const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); const reaction = '😅'; await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction }); await sleep(1000); diff --git a/packages/backend/test-federation/test/notification.test.ts b/packages/backend/test-federation/test/notification.test.ts index ea58020214b5..dd99bcc4b4bc 100644 --- a/packages/backend/test-federation/test/notification.test.ts +++ b/packages/backend/test-federation/test/notification.test.ts @@ -20,8 +20,8 @@ describe('Notification', () => { [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), - resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), + resolveRemoteUser('b.test', bob.id, aliceClient), + resolveRemoteUser('a.test', alice.id, bobClient), ]); }); diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index 0231d6af0d01..010ee309fe3c 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -20,8 +20,8 @@ describe('Timeline', () => { [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), - resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), + resolveRemoteUser('b.test', bob.id, aliceClient), + resolveRemoteUser('a.test', alice.id, bobClient), ]); await bobClient.request('following/create', { userId: aliceInBServer.id }); diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index f77d56cf503b..77623177ae3c 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -24,7 +24,7 @@ describe('User', () => { const aliceInAServer = await aliceWatcherClient.request('users/show', { userId: alice.id }); - const resolved = await resolveRemoteUser(`https://a.test/users/${aliceInAServer.id}`, aliceWatcherInBServerClient); + const resolved = await resolveRemoteUser('a.test', aliceInAServer.id, aliceWatcherInBServerClient); const aliceInBServer = await aliceWatcherInBServerClient.request('users/show', { userId: resolved.id }); @@ -55,8 +55,8 @@ describe('User', () => { [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), - resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), + resolveRemoteUser('b.test', bob.id, aliceClient), + resolveRemoteUser('a.test', alice.id, bobClient), ]); }); @@ -84,7 +84,7 @@ describe('User', () => { beforeAll(async () => { [alice, aliceClient] = await createAccount('a.test', aAdminClient); [bob, bobClient] = await createAccount('b.test', bAdminClient); - aliceInBServer = await resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient); + aliceInBServer = await resolveRemoteUser('a.test', alice.id, bobClient); await bobClient.request('following/create', { userId: aliceInBServer.id }); }); @@ -116,7 +116,7 @@ describe('User', () => { const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 1); - const pinnedNoteInBServer = await resolveRemoteNote(`https://a.test/notes/${pinnedNote.id}`, bobClient); + const pinnedNoteInBServer = await resolveRemoteNote('a.test', pinnedNote.id, bobClient); strictEqual(_aliceInBServer.pinnedNotes[0].id, pinnedNoteInBServer.id); }); @@ -140,8 +140,8 @@ describe('User', () => { [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), - resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), + resolveRemoteUser('b.test', bob.id, aliceClient), + resolveRemoteUser('a.test', alice.id, bobClient), ]); }); @@ -202,8 +202,8 @@ describe('User', () => { [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser(`https://b.test/users/${bob.id}`, aliceClient), - resolveRemoteUser(`https://a.test/users/${alice.id}`, bobClient), + resolveRemoteUser('b.test', bob.id, aliceClient), + resolveRemoteUser('a.test', alice.id, bobClient), ]); await aliceClient.request('i/update', { isLocked: true }); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 88e67fa5782b..5a82e5a57ae2 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -112,8 +112,9 @@ export async function createAccount(host: Host, adminClient: Misskey.api.APIClie ]; } -export async function resolveRemoteUser(uri: `https://${Host}/users/${string}`, fromClient: Misskey.api.APIClient): Promise { +export async function resolveRemoteUser(host: Host, id: string, fromClient: Misskey.api.APIClient): Promise { return new Promise((resolve, reject) => { + const uri = `https://${host}/users/${id}`; fromClient.request('ap/show', { uri }) .then(res => { strictEqual(res.type, 'User'); @@ -124,8 +125,9 @@ export async function resolveRemoteUser(uri: `https://${Host}/users/${string}`, }); } -export async function resolveRemoteNote(uri: `https://${Host}/notes/${string}`, fromClient: Misskey.api.APIClient): Promise { +export async function resolveRemoteNote(host: Host, id: string, fromClient: Misskey.api.APIClient): Promise { return new Promise((resolve, reject) => { + const uri = `https://${host}/notes/${id}`; fromClient.request('ap/show', { uri }) .then(res => { strictEqual(res.type, 'Note'); From 62404f82e753ca0efebf389eea939cfc3c047bb2 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 21:33:39 +0900 Subject: [PATCH 047/111] fix(block): wrong test name --- packages/backend/test-federation/test/block.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test-federation/test/block.test.ts b/packages/backend/test-federation/test/block.test.ts index 9e0080f39056..d24bfc57014d 100644 --- a/packages/backend/test-federation/test/block.test.ts +++ b/packages/backend/test-federation/test/block.test.ts @@ -10,7 +10,7 @@ const [ fetchAdmin('b.test'), ]); -describe('Blocking', () => { +describe('Block', () => { describe('Check follow', () => { let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; From dba401864c7d023e260bcc5bbcf0bae1e0e4981b Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 21:50:02 +0900 Subject: [PATCH 048/111] fix: invalid type --- packages/backend/test-federation/test/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 5a82e5a57ae2..0b22f49c5ed2 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -185,7 +185,7 @@ export async function isFired { + connection.on(type as any, ((msg: any) => { if (cond(msg)) { stream.close(); clearTimeout(timer); From e0487523074075d8ef3c06d472555f12c85a4a74 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 22:58:51 +0900 Subject: [PATCH 049/111] fix: longer timeout for fire testing --- packages/backend/test-federation/test/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 0b22f49c5ed2..442c9fd45a2b 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -196,7 +196,7 @@ export async function isFired { stream.close(); resolve(false); - }, 2000); + }, 3000); await trigger().catch(err => { stream.close(); From 29ffce2618c15ace1014414924389f359a2be0c6 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 22 Sep 2024 23:43:24 +0900 Subject: [PATCH 050/111] test(timeline): hashtag --- .../test-federation/test/timeline.test.ts | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index 010ee309fe3c..7779205d9f09 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -32,8 +32,8 @@ describe('Timeline', () => { await aliceClient.request('notes/create', { text: 'a', ...params }); } - type TimelineChannel = keyof Misskey.Channels & (`${string}Timeline` | 'antenna' | 'userList'); - type TimelineEndpoint = keyof Misskey.Endpoints & (`${string}timeline` | 'antennas/notes' | 'roles/notes'); + type TimelineChannel = keyof Misskey.Channels & (`${string}Timeline` | 'antenna' | 'userList' | 'hashtag'); + type TimelineEndpoint = keyof Misskey.Endpoints & (`${string}timeline` | 'antennas/notes' | 'roles/notes' | 'notes/search-by-tag'); const timelineMap = new Map([ ['antenna', 'antennas/notes'], ['globalTimeline', 'notes/global-timeline'], @@ -41,6 +41,7 @@ describe('Timeline', () => { ['hybridTimeline', 'notes/hybrid-timeline'], ['localTimeline', 'notes/local-timeline'], ['roleTimeline', 'roles/notes'], + ['hashtag', 'notes/search-by-tag'], ['userList', 'notes/user-list-timeline'], ]); @@ -63,6 +64,8 @@ describe('Timeline', () => { const params: Misskey.Endpoints[typeof endpoint]['req'] = endpoint === 'antennas/notes' ? { antennaId: (channelParams as Misskey.Channels['antenna']['params']).antennaId } : endpoint === 'notes/user-list-timeline' ? { listId: (channelParams as Misskey.Channels['userList']['params']).listId } : + /** @ts-expect-error @see https://github.com/misskey-dev/misskey/pull/14611 */ + endpoint === 'notes/search-by-tag' ? { query: (channelParams as Misskey.Channels['hashtag']['params']).q as string[][] } : endpoint === 'roles/notes' ? { roleId: (channelParams as Misskey.Channels['roleTimeline']['params']).roleId } : {}; const notes = await (bobClient.request as Request)(endpoint, params); @@ -167,6 +170,34 @@ describe('Timeline', () => { }); }); + describe('hashtag', () => { + const hashtag = 'hashtag'; + + test('Receive remote followee\'s note', async () => { + const tag = crypto.randomUUID(); + /** @ts-expect-error @see https://github.com/misskey-dev/misskey/pull/14611 */ + await postAndCheckReception(hashtag, true, { text: `#${tag}` }, { q: [[tag]] }); + }); + + test('Receive remote followee\'s home-only note', async () => { + const tag = crypto.randomUUID(); + /** @ts-expect-error @see https://github.com/misskey-dev/misskey/pull/14611 */ + await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'home' }, { q: [[tag]] }); + }); + + test('Receive remote followee\'s followers-only note', async () => { + const tag = crypto.randomUUID(); + /** @ts-expect-error @see https://github.com/misskey-dev/misskey/pull/14611 */ + await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'followers' }, { q: [[tag]] }); + }); + + test('Receive remote followee\'s specified-only note', async () => { + const tag = crypto.randomUUID(); + /** @ts-expect-error @see https://github.com/misskey-dev/misskey/pull/14611 */ + await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { q: [[tag]] }); + }); + }); + describe('roleTimeline', () => { const roleTimeline = 'roleTimeline'; From 7420f35c4a586ce5fd04d006c331b6e78d2f7038 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 00:07:31 +0900 Subject: [PATCH 051/111] test(note): vote delivery --- .../backend/test-federation/test/note.test.ts | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index 96168629b61b..5ad1f5328197 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -188,4 +188,57 @@ describe('Note', () => { strictEqual(reactions[0].user.id, bobInAServer.id); }); }); + + describe('Poll', () => { + describe('Any remote user\'s vote is delivered to the author', () => { + let carolClient: Misskey.api.APIClient; + + beforeAll(async () => { + [, carolClient] = await createAccount('a.test', aAdminClient); + }); + + test('Bob creates poll and receives a vote from Carol', async () => { + const note = (await bobClient.request('notes/create', { poll: { choices: ['inu', 'neko'] } })).createdNote; + const noteInAServer = await resolveRemoteNote('b.test', note.id, carolClient); + await carolClient.request('notes/polls/vote', { noteId: noteInAServer.id, choice: 0 }); + await sleep(1000); + + const noteAfterVote = await bobClient.request('notes/show', { noteId: note.id }); + assert(noteAfterVote.poll != null); + strictEqual(noteAfterVote.poll.choices[0].votes, 1); + strictEqual(noteAfterVote.poll.choices[1].votes, 0); + }); + }); + + describe('Local user\'s vote is delivered to the author\'s remote followers', () => { + let bobRemoteFollowerClient: Misskey.api.APIClient; + let localVoterClient: Misskey.api.APIClient; + + beforeAll(async () => { + [ + [, bobRemoteFollowerClient], + [, localVoterClient], + ] = await Promise.all([ + createAccount('a.test', aAdminClient), + createAccount('b.test', bAdminClient), + ]); + + await bobRemoteFollowerClient.request('following/create', { userId: bobInAServer.id }); + await sleep(1000); + }); + + test('A vote in Bob\'s server is delivered to Bob\'s remote followers', async () => { + const note = (await bobClient.request('notes/create', { poll: { choices: ['inu', 'neko'] } })).createdNote; + // NOTE: resolve before voting + const noteInAServer = await resolveRemoteNote('b.test', note.id, bobRemoteFollowerClient); + await localVoterClient.request('notes/polls/vote', { noteId: note.id, choice: 0 }); + await sleep(1000); + + const noteAfterVote = await bobRemoteFollowerClient.request('notes/show', { noteId: noteInAServer.id }); + assert(noteAfterVote.poll != null); + strictEqual(noteAfterVote.poll.choices[0].votes, 1); + strictEqual(noteAfterVote.poll.choices[1].votes, 0); + }); + }); + }); }); From c33edaa1c9c81a53d24b8c730e031f32b839bf51 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 00:21:21 +0900 Subject: [PATCH 052/111] fix: wrong description --- packages/backend/test-federation/test/block.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend/test-federation/test/block.test.ts b/packages/backend/test-federation/test/block.test.ts index d24bfc57014d..26eb2ead582a 100644 --- a/packages/backend/test-federation/test/block.test.ts +++ b/packages/backend/test-federation/test/block.test.ts @@ -149,7 +149,7 @@ describe('Block', () => { ]); }); - test('Cannot reply if blocked', async () => { + test('Cannot reaction if blocked', async () => { await aliceClient.request('blocking/create', { userId: bobInAServer.id }); await sleep(1000); @@ -165,7 +165,7 @@ describe('Block', () => { }); // FIXME: this is invalid case - test('Cannot reply even if unblocked', async () => { + test('Cannot reaction even if unblocked', async () => { // unblock here await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); await sleep(1000); @@ -183,7 +183,7 @@ describe('Block', () => { ); }); - test.skip('Can reply if unblocked', async () => { + test.skip('Can reaction if unblocked', async () => { await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); await sleep(1000); From 73a3800f194e369ec105f05f42c3f368b2935f8d Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 12:47:38 +0900 Subject: [PATCH 053/111] fix: hashtag channel param type --- packages/backend/test-federation/test/timeline.test.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index 7779205d9f09..64d335727fb0 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -64,8 +64,7 @@ describe('Timeline', () => { const params: Misskey.Endpoints[typeof endpoint]['req'] = endpoint === 'antennas/notes' ? { antennaId: (channelParams as Misskey.Channels['antenna']['params']).antennaId } : endpoint === 'notes/user-list-timeline' ? { listId: (channelParams as Misskey.Channels['userList']['params']).listId } : - /** @ts-expect-error @see https://github.com/misskey-dev/misskey/pull/14611 */ - endpoint === 'notes/search-by-tag' ? { query: (channelParams as Misskey.Channels['hashtag']['params']).q as string[][] } : + endpoint === 'notes/search-by-tag' ? { query: (channelParams as Misskey.Channels['hashtag']['params']).q } : endpoint === 'roles/notes' ? { roleId: (channelParams as Misskey.Channels['roleTimeline']['params']).roleId } : {}; const notes = await (bobClient.request as Request)(endpoint, params); @@ -175,25 +174,21 @@ describe('Timeline', () => { test('Receive remote followee\'s note', async () => { const tag = crypto.randomUUID(); - /** @ts-expect-error @see https://github.com/misskey-dev/misskey/pull/14611 */ await postAndCheckReception(hashtag, true, { text: `#${tag}` }, { q: [[tag]] }); }); test('Receive remote followee\'s home-only note', async () => { const tag = crypto.randomUUID(); - /** @ts-expect-error @see https://github.com/misskey-dev/misskey/pull/14611 */ await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'home' }, { q: [[tag]] }); }); test('Receive remote followee\'s followers-only note', async () => { const tag = crypto.randomUUID(); - /** @ts-expect-error @see https://github.com/misskey-dev/misskey/pull/14611 */ await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'followers' }, { q: [[tag]] }); }); test('Receive remote followee\'s specified-only note', async () => { const tag = crypto.randomUUID(); - /** @ts-expect-error @see https://github.com/misskey-dev/misskey/pull/14611 */ await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { q: [[tag]] }); }); }); From 84b4db4235d7e4eb1a6a18a369c37ebbab3e912e Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 12:59:42 +0900 Subject: [PATCH 054/111] refactor: wrap basic cases --- .../test-federation/test/timeline.test.ts | 182 ++++++++++-------- 1 file changed, 99 insertions(+), 83 deletions(-) diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index 64d335727fb0..ceb924dc65c8 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -76,68 +76,76 @@ describe('Timeline', () => { // NOTE: narrowing scope intentionally to prevent mistakes by copy-and-paste const homeTimeline = 'homeTimeline'; - test('Receive remote followee\'s note', async () => { - await postAndCheckReception(homeTimeline, true); - }); + describe('Check reception of remote followee\'s Note', () => { + test('Receive remote followee\'s Note', async () => { + await postAndCheckReception(homeTimeline, true); + }); - test('Receive remote followee\'s home-only note', async () => { - await postAndCheckReception(homeTimeline, true, { visibility: 'home' }); - }); + test('Receive remote followee\'s home-only Note', async () => { + await postAndCheckReception(homeTimeline, true, { visibility: 'home' }); + }); - test('Receive remote followee\'s followers-only note', async () => { - await postAndCheckReception(homeTimeline, true, { visibility: 'followers' }); - }); + test('Receive remote followee\'s followers-only Note', async () => { + await postAndCheckReception(homeTimeline, true, { visibility: 'followers' }); + }); - test('Receive remote followee\'s specified-only note', async () => { - await postAndCheckReception(homeTimeline, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + test('Receive remote followee\'s specified-only Note', async () => { + await postAndCheckReception(homeTimeline, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + }); }); }); describe('localTimeline', () => { const localTimeline = 'localTimeline'; - test('Don\'t receive remote followee\'s note', async () => { - await postAndCheckReception(localTimeline, false, { visibility: 'followers', visibleUserIds: [bobInAServer.id] }); + describe('Check reception of remote followee\'s Note', () => { + test('Don\'t receive remote followee\'s Note', async () => { + await postAndCheckReception(localTimeline, false, { visibility: 'followers', visibleUserIds: [bobInAServer.id] }); + }); }); }); describe('hybridTimeline', () => { const hybridTimeline = 'hybridTimeline'; - test('Receive remote followee\'s note', async () => { - await postAndCheckReception(hybridTimeline, true); - }); + describe('Check reception of remote followee\'s Note', () => { + test('Receive remote followee\'s Note', async () => { + await postAndCheckReception(hybridTimeline, true); + }); - test('Receive remote followee\'s home-only note', async () => { - await postAndCheckReception(hybridTimeline, true, { visibility: 'home' }); - }); + test('Receive remote followee\'s home-only Note', async () => { + await postAndCheckReception(hybridTimeline, true, { visibility: 'home' }); + }); - test('Receive remote followee\'s followers-only note', async () => { - await postAndCheckReception(hybridTimeline, true, { visibility: 'followers' }); - }); + test('Receive remote followee\'s followers-only Note', async () => { + await postAndCheckReception(hybridTimeline, true, { visibility: 'followers' }); + }); - test('Receive remote followee\'s specified-only note', async () => { - await postAndCheckReception(hybridTimeline, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + test('Receive remote followee\'s specified-only Note', async () => { + await postAndCheckReception(hybridTimeline, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + }); }); }); describe('globalTimeline', () => { const globalTimeline = 'globalTimeline'; - test('Receive remote followee\'s note', async () => { - await postAndCheckReception(globalTimeline, true); - }); + describe('Check reception of remote followee\'s Note', () => { + test('Receive remote followee\'s Note', async () => { + await postAndCheckReception(globalTimeline, true); + }); - test('Don\'t receive remote followee\'s home-only note', async () => { - await postAndCheckReception(globalTimeline, false, { visibility: 'home' }); - }); + test('Don\'t receive remote followee\'s home-only Note', async () => { + await postAndCheckReception(globalTimeline, false, { visibility: 'home' }); + }); - test('Don\'t receive remote followee\'s followers-only note', async () => { - await postAndCheckReception(globalTimeline, false, { visibility: 'followers' }); - }); + test('Don\'t receive remote followee\'s followers-only Note', async () => { + await postAndCheckReception(globalTimeline, false, { visibility: 'followers' }); + }); - test('Don\'t receive remote followee\'s specified-only note', async () => { - await postAndCheckReception(globalTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + test('Don\'t receive remote followee\'s specified-only Note', async () => { + await postAndCheckReception(globalTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + }); }); }); @@ -152,44 +160,48 @@ describe('Timeline', () => { await sleep(1000); }); - test('Receive remote followee\'s note', async () => { - await postAndCheckReception(userList, true, {}, { listId: list.id }); - }); + describe('Check reception of remote followee\'s Note', () => { + test('Receive remote followee\'s Note', async () => { + await postAndCheckReception(userList, true, {}, { listId: list.id }); + }); - test('Receive remote followee\'s home-only note', async () => { - await postAndCheckReception(userList, true, { visibility: 'home' }, { listId: list.id }); - }); + test('Receive remote followee\'s home-only Note', async () => { + await postAndCheckReception(userList, true, { visibility: 'home' }, { listId: list.id }); + }); - test('Receive remote followee\'s followers-only note', async () => { - await postAndCheckReception(userList, true, { visibility: 'followers' }, { listId: list.id }); - }); + test('Receive remote followee\'s followers-only Note', async () => { + await postAndCheckReception(userList, true, { visibility: 'followers' }, { listId: list.id }); + }); - test('Receive remote followee\'s specified-only note', async () => { - await postAndCheckReception(userList, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { listId: list.id }); + test('Receive remote followee\'s specified-only Note', async () => { + await postAndCheckReception(userList, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { listId: list.id }); + }); }); }); describe('hashtag', () => { const hashtag = 'hashtag'; - test('Receive remote followee\'s note', async () => { - const tag = crypto.randomUUID(); - await postAndCheckReception(hashtag, true, { text: `#${tag}` }, { q: [[tag]] }); - }); + describe('Check reception of remote followee\'s Note', () => { + test('Receive remote followee\'s Note', async () => { + const tag = crypto.randomUUID(); + await postAndCheckReception(hashtag, true, { text: `#${tag}` }, { q: [[tag]] }); + }); - test('Receive remote followee\'s home-only note', async () => { - const tag = crypto.randomUUID(); - await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'home' }, { q: [[tag]] }); - }); + test('Receive remote followee\'s home-only Note', async () => { + const tag = crypto.randomUUID(); + await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'home' }, { q: [[tag]] }); + }); - test('Receive remote followee\'s followers-only note', async () => { - const tag = crypto.randomUUID(); - await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'followers' }, { q: [[tag]] }); - }); + test('Receive remote followee\'s followers-only Note', async () => { + const tag = crypto.randomUUID(); + await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'followers' }, { q: [[tag]] }); + }); - test('Receive remote followee\'s specified-only note', async () => { - const tag = crypto.randomUUID(); - await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { q: [[tag]] }); + test('Receive remote followee\'s specified-only Note', async () => { + const tag = crypto.randomUUID(); + await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { q: [[tag]] }); + }); }); }); @@ -220,20 +232,22 @@ describe('Timeline', () => { await sleep(1000); }); - test('Receive remote followee\'s note', async () => { - await postAndCheckReception(roleTimeline, true, {}, { roleId: role.id }); - }); + describe('Check reception of remote followee\'s Note', () => { + test('Receive remote followee\'s Note', async () => { + await postAndCheckReception(roleTimeline, true, {}, { roleId: role.id }); + }); - test('Don\'t receive remote followee\'s home-only note', async () => { - await postAndCheckReception(roleTimeline, false, { visibility: 'home' }, { roleId: role.id }); - }); + test('Don\'t receive remote followee\'s home-only Note', async () => { + await postAndCheckReception(roleTimeline, false, { visibility: 'home' }, { roleId: role.id }); + }); - test('Don\'t receive remote followee\'s followers-only note', async () => { - await postAndCheckReception(roleTimeline, false, { visibility: 'followers' }, { roleId: role.id }); - }); + test('Don\'t receive remote followee\'s followers-only Note', async () => { + await postAndCheckReception(roleTimeline, false, { visibility: 'followers' }, { roleId: role.id }); + }); - test('Don\'t receive remote followee\'s specified-only note', async () => { - await postAndCheckReception(roleTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { roleId: role.id }); + test('Don\'t receive remote followee\'s specified-only Note', async () => { + await postAndCheckReception(roleTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { roleId: role.id }); + }); }); afterAll(async () => { @@ -262,20 +276,22 @@ describe('Timeline', () => { await sleep(1000); }); - test('Receive remote followee\'s note', async () => { - await postAndCheckReception(antenna, true, { text: 'I love Bob (1)' }, { antennaId: bobAntenna.id }); - }); + describe('Check reception of remote followee\'s Note', () => { + test('Receive remote followee\'s Note', async () => { + await postAndCheckReception(antenna, true, { text: 'I love Bob (1)' }, { antennaId: bobAntenna.id }); + }); - test('Don\'t receive remote followee\'s home-only note', async () => { - await postAndCheckReception(antenna, false, { text: 'I love Bob (2)', visibility: 'home' }, { antennaId: bobAntenna.id }); - }); + test('Don\'t receive remote followee\'s home-only Note', async () => { + await postAndCheckReception(antenna, false, { text: 'I love Bob (2)', visibility: 'home' }, { antennaId: bobAntenna.id }); + }); - test('Don\'t receive remote followee\'s followers-only note', async () => { - await postAndCheckReception(antenna, false, { text: 'I love Bob (3)', visibility: 'followers' }, { antennaId: bobAntenna.id }); - }); + test('Don\'t receive remote followee\'s followers-only Note', async () => { + await postAndCheckReception(antenna, false, { text: 'I love Bob (3)', visibility: 'followers' }, { antennaId: bobAntenna.id }); + }); - test('Don\'t receive remote followee\'s specified-only note', async () => { - await postAndCheckReception(antenna, false, { text: 'I love Bob (4)', visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { antennaId: bobAntenna.id }); + test('Don\'t receive remote followee\'s specified-only Note', async () => { + await postAndCheckReception(antenna, false, { text: 'I love Bob (4)', visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { antennaId: bobAntenna.id }); + }); }); afterAll(async () => { From 24b3d16d09ee5e7c5f8dad20edc06a6adfd7f621 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 13:21:50 +0900 Subject: [PATCH 055/111] test(timeline): add homeTimeline tests --- .../test-federation/test/timeline.test.ts | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index ceb924dc65c8..84fd4ab8a7e8 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -12,12 +12,12 @@ const [ describe('Timeline', () => { let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, aliceClient] = await createAccount('a.test', aAdminClient); - [bob, bobClient] = await createAccount('b.test', bAdminClient); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ resolveRemoteUser('b.test', bob.id, aliceClient), @@ -89,9 +89,25 @@ describe('Timeline', () => { await postAndCheckReception(homeTimeline, true, { visibility: 'followers' }); }); - test('Receive remote followee\'s specified-only Note', async () => { + test('Receive remote followee\'s visible specified-only Note', async () => { await postAndCheckReception(homeTimeline, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); }); + + test('Don\'t receive remote followee\'s localOnly Note', async () => { + await postAndCheckReception(homeTimeline, false, { localOnly: true }); + }); + + test('Don\'t receive remote followee\'s invisible specified-only Note', async () => { + await postAndCheckReception(homeTimeline, false, { visibility: 'specified' }); + }); + + /** + * FIXME: can receive this + * @see https://github.com/misskey-dev/misskey/issues/14083 + */ + test.failing('Don\'t receive remote followee\'s invisible and mentioned specified-only Note', async () => { + await postAndCheckReception(homeTimeline, false, { text: `@${bobUsername}@b.test Hello`, visibility: 'specified' }); + }); }); }); From 124f8f8cf5fb6382078dae0baa718b29c9091418 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 13:23:49 +0900 Subject: [PATCH 056/111] fix(timeline): correct wrong case and description --- .../backend/test-federation/test/timeline.test.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index 84fd4ab8a7e8..97ec786a65b2 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -116,7 +116,7 @@ describe('Timeline', () => { describe('Check reception of remote followee\'s Note', () => { test('Don\'t receive remote followee\'s Note', async () => { - await postAndCheckReception(localTimeline, false, { visibility: 'followers', visibleUserIds: [bobInAServer.id] }); + await postAndCheckReception(localTimeline, false); }); }); }); @@ -137,7 +137,7 @@ describe('Timeline', () => { await postAndCheckReception(hybridTimeline, true, { visibility: 'followers' }); }); - test('Receive remote followee\'s specified-only Note', async () => { + test('Receive remote followee\'s visible specified-only Note', async () => { await postAndCheckReception(hybridTimeline, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); }); }); @@ -159,7 +159,7 @@ describe('Timeline', () => { await postAndCheckReception(globalTimeline, false, { visibility: 'followers' }); }); - test('Don\'t receive remote followee\'s specified-only Note', async () => { + test('Don\'t receive remote followee\'s visible specified-only Note', async () => { await postAndCheckReception(globalTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); }); }); @@ -189,7 +189,7 @@ describe('Timeline', () => { await postAndCheckReception(userList, true, { visibility: 'followers' }, { listId: list.id }); }); - test('Receive remote followee\'s specified-only Note', async () => { + test('Receive remote followee\'s visible specified-only Note', async () => { await postAndCheckReception(userList, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { listId: list.id }); }); }); @@ -214,7 +214,7 @@ describe('Timeline', () => { await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'followers' }, { q: [[tag]] }); }); - test('Receive remote followee\'s specified-only Note', async () => { + test('Receive remote followee\'s visible specified-only Note', async () => { const tag = crypto.randomUUID(); await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { q: [[tag]] }); }); @@ -261,7 +261,7 @@ describe('Timeline', () => { await postAndCheckReception(roleTimeline, false, { visibility: 'followers' }, { roleId: role.id }); }); - test('Don\'t receive remote followee\'s specified-only Note', async () => { + test('Don\'t receive remote followee\'s visible specified-only Note', async () => { await postAndCheckReception(roleTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { roleId: role.id }); }); }); @@ -305,7 +305,7 @@ describe('Timeline', () => { await postAndCheckReception(antenna, false, { text: 'I love Bob (3)', visibility: 'followers' }, { antennaId: bobAntenna.id }); }); - test('Don\'t receive remote followee\'s specified-only Note', async () => { + test('Don\'t receive remote followee\'s visible specified-only Note', async () => { await postAndCheckReception(antenna, false, { text: 'I love Bob (4)', visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { antennaId: bobAntenna.id }); }); }); From c67ac4cc0e734a8990a116c847a05f1f9b64bd8f Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:31:15 +0900 Subject: [PATCH 057/111] test(notification): add tests for Note --- .../test-federation/test/notification.test.ts | 115 +++++++++++++++++- 1 file changed, 112 insertions(+), 3 deletions(-) diff --git a/packages/backend/test-federation/test/notification.test.ts b/packages/backend/test-federation/test/notification.test.ts index dd99bcc4b4bc..26a41f145d81 100644 --- a/packages/backend/test-federation/test/notification.test.ts +++ b/packages/backend/test-federation/test/notification.test.ts @@ -1,6 +1,6 @@ import { deepStrictEqual, strictEqual } from 'assert'; import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, isFired, resolveRemoteUser } from './utils.js'; +import { createAccount, fetchAdmin, isFired, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; const [ [, aAdminClient], @@ -11,12 +11,12 @@ const [ ]); describe('Notification', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test', aAdminClient); + [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); [bob, bobClient] = await createAccount('b.test', bAdminClient); [bobInAServer, aliceInBServer] = await Promise.all([ @@ -55,5 +55,114 @@ describe('Notification', () => { strictEqual(notification.userId, bobInAServer.id); } }); + + afterAll(async () => await bobClient.request('following/delete', { userId: aliceInBServer.id })); + }); + + describe('Note', () => { + test('Get notification when get a reaction', async () => { + const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bobClient); + const reaction = '😅'; + const fired = await isFired( + 'a.test', alice, 'main', + async () => await bobClient.request('notes/reactions/create', { noteId: noteInBServer.id, reaction }), + 'notification', msg => { + return msg.type === 'reaction' && msg.note.id === note.id && msg.userId === bobInAServer.id && msg.reaction === reaction; + }, + ); + strictEqual(fired, true); + }); + + test('Get notification when replied', async () => { + const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bobClient); + const text = crypto.randomUUID(); + const fired = await Promise.all([ + isFired( + 'a.test', alice, 'main', + async () => { + await sleep(200); + await bobClient.request('notes/create', { text, replyId: noteInBServer.id }); + }, + 'notification', msg => { + return msg.type === 'reply' && msg.note.reply!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; + }, + ), + isFired( + 'a.test', alice, 'main', + async () => {}, // NOTE: do nothing because already done above + 'reply', msg => msg.reply!.id === note.id && msg.userId === bobInAServer.id && msg.text === text, + ), + ]); + deepStrictEqual(fired, [true, true]); + }); + + test('Get notification when renoted', async () => { + const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bobClient); + const fired = await Promise.all([ + isFired( + 'a.test', alice, 'main', + async () => { + await sleep(200); + await bobClient.request('notes/create', { renoteId: noteInBServer.id }); + }, + 'notification', msg => { + return msg.type === 'renote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id; + }, + ), + isFired( + 'a.test', alice, 'main', + async () => {}, // NOTE: do nothing because already done above + 'renote', msg => msg.renote!.id === note.id && msg.userId === bobInAServer.id, + ), + ]); + deepStrictEqual(fired, [true, true]); + }); + + test('Get notification when quoted', async () => { + const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bobClient); + const text = crypto.randomUUID(); + const fired = await Promise.all([ + isFired( + 'a.test', alice, 'main', + async () => { + await sleep(200); + await bobClient.request('notes/create', { text, renoteId: noteInBServer.id }); + }, + 'notification', msg => { + return msg.type === 'quote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; + }, + ), + isFired( + 'a.test', alice, 'main', + async () => {}, // NOTE: do nothing because already done above + 'renote', msg => msg.renote!.id === note.id && msg.userId === bobInAServer.id && msg.text === text, + ), + ]); + deepStrictEqual(fired, [true, true]); + }); + + test('Get notification when get a reaction', async () => { + const text = `@${aliceUsername}@a.test`; + const fired = await Promise.all([ + isFired( + 'a.test', alice, 'main', + async () => { + await sleep(200); + await bobClient.request('notes/create', { text }); + }, + 'notification', msg => msg.type === 'mention' && msg.userId === bobInAServer.id && msg.note.text === text, + ), + isFired( + 'a.test', alice, 'main', + async () => {}, // NOTE: do nothing because already done above + 'mention', msg => msg.userId === bobInAServer.id && msg.text === text, + ), + ]); + deepStrictEqual(fired, [true, true]); + }); }); }); From b94846100040c2cd7304e82513a4321f42d15bf5 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:17:26 +0900 Subject: [PATCH 058/111] refactor(user): wrap profile consistency with describe --- .../backend/test-federation/test/user.test.ts | 64 ++++++++++--------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index 77623177ae3c..94f4684f89ea 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -12,37 +12,43 @@ const [ describe('User', () => { describe('Profile', () => { - test('Consistency of profile', async () => { - const [alice] = await createAccount('a.test', aAdminClient); - const [ - [, aliceWatcherClient], - [, aliceWatcherInBServerClient], - ] = await Promise.all([ - createAccount('a.test', aAdminClient), - createAccount('b.test', bAdminClient), - ]); - - const aliceInAServer = await aliceWatcherClient.request('users/show', { userId: alice.id }); - - const resolved = await resolveRemoteUser('a.test', aliceInAServer.id, aliceWatcherInBServerClient); + describe('Consistency of profile', () => { + let alice: Misskey.entities.SigninResponse; + let aliceWatcherClient: Misskey.api.APIClient; + let aliceWatcherInBServerClient: Misskey.api.APIClient; - const aliceInBServer = await aliceWatcherInBServerClient.request('users/show', { userId: resolved.id }); - - // console.log(`a.test: ${JSON.stringify(aliceInAServer, null, '\t')}`); - // console.log(`b.test: ${JSON.stringify(aliceInBServer, null, '\t')}`); + beforeAll(async () => { + [alice] = await createAccount('a.test', aAdminClient); + [ + [, aliceWatcherClient], + [, aliceWatcherInBServerClient], + ] = await Promise.all([ + createAccount('a.test', aAdminClient), + createAccount('b.test', bAdminClient), + ]); + }); - deepStrictEqualWithExcludedFields(aliceInAServer, aliceInBServer, [ - 'id', - 'host', - 'avatarUrl', - 'instance', - 'badgeRoles', - 'url', - 'uri', - 'createdAt', - 'lastFetchedAt', - 'publicReactions', - ]); + test('Check consistency', async () => { + const aliceInAServer = await aliceWatcherClient.request('users/show', { userId: alice.id }); + const resolved = await resolveRemoteUser('a.test', aliceInAServer.id, aliceWatcherInBServerClient); + const aliceInBServer = await aliceWatcherInBServerClient.request('users/show', { userId: resolved.id }); + + // console.log(`a.test: ${JSON.stringify(aliceInAServer, null, '\t')}`); + // console.log(`b.test: ${JSON.stringify(aliceInBServer, null, '\t')}`); + + deepStrictEqualWithExcludedFields(aliceInAServer, aliceInBServer, [ + 'id', + 'host', + 'avatarUrl', + 'instance', + 'badgeRoles', + 'url', + 'uri', + 'createdAt', + 'lastFetchedAt', + 'publicReactions', + ]); + }); }); describe('isCat is federated', () => { From 36381ac37af36c845d398254bba5057f2600d777 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:19:06 +0900 Subject: [PATCH 059/111] chore(note): add issue link --- packages/backend/test-federation/test/note.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index 5ad1f5328197..d6737bce2475 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -45,6 +45,7 @@ describe('Note', () => { /** Consistency of files is checked at {@link file://./drive.test.ts}, so let's skip. */ 'fileIds', 'files', + /** @see https://github.com/misskey-dev/misskey/issues/12409 */ 'reactionAcceptance', 'userId', 'user', From 256f5e5b2c2c16d8340ede1f8f6352563efe43db Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 18:06:48 +0900 Subject: [PATCH 060/111] test(timeline): add test --- packages/backend/test-federation/test/timeline.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index 97ec786a65b2..1b02ee809e5d 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -108,6 +108,15 @@ describe('Timeline', () => { test.failing('Don\'t receive remote followee\'s invisible and mentioned specified-only Note', async () => { await postAndCheckReception(homeTimeline, false, { text: `@${bobUsername}@b.test Hello`, visibility: 'specified' }); }); + + /** + * FIXME: cannot receive this + * @see https://github.com/misskey-dev/misskey/issues/14084 + */ + test.failing('Receive remote followee\'s visible specified-only reply to invisible specified-only Note', async () => { + const note = (await aliceClient.request('notes/create', { text: 'a', visibility: 'specified' })).createdNote; + await postAndCheckReception(homeTimeline, true, { replyId: note.id, visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + }); }); }); From 66255e2a92e926242fec7cf0dad96c0ef393c24d Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 18:58:42 +0900 Subject: [PATCH 061/111] test(user): suspension --- .../backend/test-federation/test/user.test.ts | 107 +++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index 94f4684f89ea..50e97f03edb3 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -1,4 +1,4 @@ -import { rejects, strictEqual } from 'node:assert'; +import assert, { rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; @@ -285,4 +285,109 @@ describe('User', () => { }); }); }); + + describe('Suspension', () => { + describe('Check suspend/unsuspend consistency', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, aliceClient] = await createAccount('a.test', aAdminClient); + [bob, bobClient] = await createAccount('b.test', bAdminClient); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, aliceClient), + resolveRemoteUser('a.test', alice.id, bobClient), + ]); + }); + + test('Bob follows Alice, and Alice gets suspended, there is no following relation, and Bob fails to follow again', async () => { + await bobClient.request('following/create', { userId: aliceInBServer.id }); + await sleep(1000); + + const followers = await aliceClient.request('users/followers', { userId: alice.id }); + strictEqual(followers.length, 1); // followed by Bob + + await aAdminClient.request('admin/suspend-user', { userId: alice.id }); + await sleep(1000); + + const following = await bobClient.request('users/following', { userId: bob.id }); + strictEqual(following.length, 0); // no following relation + + await rejects( + async () => await bobClient.request('following/create', { userId: aliceInBServer.id }), + (err: any) => { + strictEqual(err.code, 'NO_SUCH_USER'); + return true; + }, + ); + }); + + test('Alice gets unsuspended, Bob succeeds in following Alice', async () => { + await aAdminClient.request('admin/unsuspend-user', { userId: alice.id }); + await sleep(1000); + + const followers = await aliceClient.request('users/followers', { userId: alice.id }); + strictEqual(followers.length, 1); // FIXME: followers are not deleted?? + + /** + * FIXME: still rejected! + * seems to can't process Undo Delete activity because it is not implemented + * related @see https://github.com/misskey-dev/misskey/issues/13273 + */ + await rejects( + async () => await bobClient.request('following/create', { userId: aliceInBServer.id }), + (err: any) => { + strictEqual(err.code, 'NO_SUCH_USER'); + return true; + }, + ); + + // FIXME: resolving also fails + await rejects( + async () => await resolveRemoteUser('a.test', alice.id, bobClient), + (err: any) => { + strictEqual(err.code, 'INTERNAL_ERROR'); + return true; + }, + ); + }); + + /** + * instead of simple unsuspension, let's tell existence by following from Alice + */ + test('Alice can follow Bob', async () => { + await aliceClient.request('following/create', { userId: bobInAServer.id }); + await sleep(1000); + + const bobFollowers = await bobClient.request('users/followers', { userId: bob.id }); + strictEqual(bobFollowers.length, 1); // followed by Alice + assert(bobFollowers[0].follower != null); + const renewedAliceInBServer = bobFollowers[0].follower; + assert(aliceInBServer.username === renewedAliceInBServer.username); + assert(aliceInBServer.host === renewedAliceInBServer.host); + assert(aliceInBServer.id !== renewedAliceInBServer.id); // TODO: Same username and host, but their ids are different! Is it OK? + + const following = await bobClient.request('users/following', { userId: bob.id }); + strictEqual(following.length, 0); // following are deleted + + // Bob tries to follow Alice + await bobClient.request('following/create', { userId: renewedAliceInBServer.id }); + await sleep(1000); + + const aliceFollowers = await aliceClient.request('users/followers', { userId: alice.id }); + strictEqual(aliceFollowers.length, 1); + + // FIXME: but resolving still fails ... + await rejects( + async () => await resolveRemoteUser('a.test', alice.id, bobClient), + (err: any) => { + strictEqual(err.code, 'INTERNAL_ERROR'); + return true; + }, + ); + }); + }); + }); }); From a41335f8020c20606c43b2c3b9056c0f6b52e29b Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 19:24:22 +0900 Subject: [PATCH 062/111] test: emoji --- .../test-federation/test/emoji.test.ts | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 packages/backend/test-federation/test/emoji.test.ts diff --git a/packages/backend/test-federation/test/emoji.test.ts b/packages/backend/test-federation/test/emoji.test.ts new file mode 100644 index 000000000000..abcb76b4eb6e --- /dev/null +++ b/packages/backend/test-federation/test/emoji.test.ts @@ -0,0 +1,59 @@ +import assert, { strictEqual } from 'assert'; +import * as Misskey from 'misskey-js'; +import { createAccount, fetchAdmin, resolveRemoteUser, sleep, uploadFile } from './utils.js'; + +const [ + [aAdmin, aAdminClient], + [, bAdminClient], +] = await Promise.all([ + fetchAdmin('a.test'), + fetchAdmin('b.test'), +]); + +describe('Emoji', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, aliceClient] = await createAccount('a.test', aAdminClient); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, aliceClient), + resolveRemoteUser('a.test', alice.id, bobClient), + ]); + + await bobClient.request('following/create', { userId: aliceInBServer.id }); + await sleep(1000); + }); + + test('A server creates a custom emoji, and Bob can resolve it from Note', async () => { + const name = crypto.randomUUID().replaceAll('-', ''); + const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); + await aAdminClient.request('admin/emoji/add', { name, fileId: file.id }); + const note = (await aliceClient.request('notes/create', { text: `I love :${name}:` })).createdNote; + await sleep(1000); + + const notes = await bobClient.request('notes/timeline', {}); + strictEqual(notes.length, 1); + const noteInBServer = notes[0]; + + assert(note.text !== noteInBServer.text); // TODO: why? + assert(noteInBServer.emojis != null); + assert(name in noteInBServer.emojis); + assert(noteInBServer.emojis[name] === file.url); + }); + + test('A server creates a custom emoji, and Bob can resolve it from Profile', async () => { + const name = crypto.randomUUID().replaceAll('-', ''); + const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); + await aAdminClient.request('admin/emoji/add', { name, fileId: file.id }); + await aliceClient.request('i/update', { name: `:${name}:` }); + await sleep(1000); + + const renewedAliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); + assert(name in renewedAliceInBServer.emojis); + assert(renewedAliceInBServer.emojis[name] === file.url); + }); +}); From 30f2c01f408fc654fe7ead15fead1dcf0751518e Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 22:57:10 +0900 Subject: [PATCH 063/111] refactor: fetch admin first --- .../test-federation/test/block.test.ts | 22 ++++-------- .../test-federation/test/drive.test.ts | 10 ++---- .../test-federation/test/emoji.test.ts | 12 ++----- .../backend/test-federation/test/move.test.ts | 20 ++++------- .../backend/test-federation/test/note.test.ts | 22 ++++-------- .../test-federation/test/notification.test.ts | 14 ++------ .../test-federation/test/timeline.test.ts | 12 ++----- .../backend/test-federation/test/user.test.ts | 34 ++++++++----------- .../backend/test-federation/test/utils.ts | 25 +++++++++----- 9 files changed, 61 insertions(+), 110 deletions(-) diff --git a/packages/backend/test-federation/test/block.test.ts b/packages/backend/test-federation/test/block.test.ts index 26eb2ead582a..c9cf8421a005 100644 --- a/packages/backend/test-federation/test/block.test.ts +++ b/packages/backend/test-federation/test/block.test.ts @@ -1,14 +1,6 @@ import { deepStrictEqual, rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; - -const [ - [, aAdminClient], - [, bAdminClient], -] = await Promise.all([ - fetchAdmin('a.test'), - fetchAdmin('b.test'), -]); +import { createAccount, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; describe('Block', () => { describe('Check follow', () => { @@ -17,8 +9,8 @@ describe('Block', () => { let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test', aAdminClient); - [bob, bobClient] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test'); + [bob, bobClient] = await createAccount('b.test'); [bobInAServer, aliceInBServer] = await Promise.all([ resolveRemoteUser('b.test', bob.id, aliceClient), @@ -98,8 +90,8 @@ describe('Block', () => { let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test', aAdminClient); - [bob, bobClient] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test'); + [bob, bobClient] = await createAccount('b.test'); [bobInAServer, aliceInBServer] = await Promise.all([ resolveRemoteUser('b.test', bob.id, aliceClient), @@ -140,8 +132,8 @@ describe('Block', () => { let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test', aAdminClient); - [bob, bobClient] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test'); + [bob, bobClient] = await createAccount('b.test'); [bobInAServer, aliceInBServer] = await Promise.all([ resolveRemoteUser('b.test', bob.id, aliceClient), diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index 170e0f83fad1..80d42503b75c 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -2,20 +2,14 @@ import assert, { strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, uploadFile } from './utils.js'; -const [ - [, aAdminClient], - [, bAdminClient], -] = await Promise.all([ - fetchAdmin('a.test'), - fetchAdmin('b.test'), -]); +const [, bAdminClient] = await fetchAdmin('b.test'); describe('Drive', () => { describe('Upload image in a.test and resolve from b.test', () => { let uploader: Misskey.entities.SigninResponse, uploaderClient: Misskey.api.APIClient; beforeAll(async () => { - [uploader, uploaderClient] = await createAccount('a.test', aAdminClient); + [uploader, uploaderClient] = await createAccount('a.test'); }); let image: Misskey.entities.DriveFile, imageInBServer: Misskey.entities.DriveFile; diff --git a/packages/backend/test-federation/test/emoji.test.ts b/packages/backend/test-federation/test/emoji.test.ts index abcb76b4eb6e..1a416a62a06a 100644 --- a/packages/backend/test-federation/test/emoji.test.ts +++ b/packages/backend/test-federation/test/emoji.test.ts @@ -2,13 +2,7 @@ import assert, { strictEqual } from 'assert'; import * as Misskey from 'misskey-js'; import { createAccount, fetchAdmin, resolveRemoteUser, sleep, uploadFile } from './utils.js'; -const [ - [aAdmin, aAdminClient], - [, bAdminClient], -] = await Promise.all([ - fetchAdmin('a.test'), - fetchAdmin('b.test'), -]); +const [aAdmin, aAdminClient] = await fetchAdmin('a.test'); describe('Emoji', () => { let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; @@ -16,8 +10,8 @@ describe('Emoji', () => { let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test', aAdminClient); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test'); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test'); [bobInAServer, aliceInBServer] = await Promise.all([ resolveRemoteUser('b.test', bob.id, aliceClient), diff --git a/packages/backend/test-federation/test/move.test.ts b/packages/backend/test-federation/test/move.test.ts index 74c316c2dd79..e4cf30e24adc 100644 --- a/packages/backend/test-federation/test/move.test.ts +++ b/packages/backend/test-federation/test/move.test.ts @@ -1,19 +1,11 @@ import assert, { strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, sleep } from './utils.js'; - -const [ - [, aAdminClient], - [, bAdminClient], -] = await Promise.all([ - fetchAdmin('a.test'), - fetchAdmin('b.test'), -]); +import { createAccount, sleep } from './utils.js'; describe('Move', () => { test('Minimum move', async () => { - const [, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - const [, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + const [, aliceClient, { username: aliceUsername }] = await createAccount('a.test'); + const [, bobClient, { username: bobUsername }] = await createAccount('b.test'); await bobClient.request('i/update', { alsoKnownAs: [`@${aliceUsername}@a.test`] }); await aliceClient.request('i/move', { moveToAccount: `@${bobUsername}@b.test` }); @@ -26,9 +18,9 @@ describe('Move', () => { let carol: Misskey.entities.SigninResponse, carolClient: Misskey.api.APIClient, carolUsername: string; beforeAll(async () => { - [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); - [carol, carolClient, { username: carolUsername }] = await createAccount('a.test', aAdminClient); + [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test'); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test'); + [carol, carolClient, { username: carolUsername }] = await createAccount('a.test'); // Follow @carol@a.test ==> @alice@a.test await carolClient.request('following/create', { userId: alice.id }); diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index d6737bce2475..e4d72a616bda 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -1,14 +1,6 @@ import assert, { rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, resolveRemoteUser, sleep, uploadFile } from './utils.js'; - -const [ - [, aAdminClient], - [, bAdminClient], -] = await Promise.all([ - fetchAdmin('a.test'), - fetchAdmin('b.test'), -]); +import { createAccount, deepStrictEqualWithExcludedFields, resolveRemoteNote, resolveRemoteUser, sleep, uploadFile } from './utils.js'; describe('Note', () => { let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; @@ -16,8 +8,8 @@ describe('Note', () => { let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test', aAdminClient); - [bob, bobClient] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test'); + [bob, bobClient] = await createAccount('b.test'); [bobInAServer, aliceInBServer] = await Promise.all([ resolveRemoteUser('b.test', bob.id, aliceClient), @@ -153,7 +145,7 @@ describe('Note', () => { let carolClient: Misskey.api.APIClient; beforeAll(async () => { - [, carolClient] = await createAccount('a.test', aAdminClient); + [, carolClient] = await createAccount('a.test'); await carolClient.request('following/create', { userId: bobInAServer.id }); await sleep(1000); @@ -195,7 +187,7 @@ describe('Note', () => { let carolClient: Misskey.api.APIClient; beforeAll(async () => { - [, carolClient] = await createAccount('a.test', aAdminClient); + [, carolClient] = await createAccount('a.test'); }); test('Bob creates poll and receives a vote from Carol', async () => { @@ -220,8 +212,8 @@ describe('Note', () => { [, bobRemoteFollowerClient], [, localVoterClient], ] = await Promise.all([ - createAccount('a.test', aAdminClient), - createAccount('b.test', bAdminClient), + createAccount('a.test'), + createAccount('b.test'), ]); await bobRemoteFollowerClient.request('following/create', { userId: bobInAServer.id }); diff --git a/packages/backend/test-federation/test/notification.test.ts b/packages/backend/test-federation/test/notification.test.ts index 26a41f145d81..eadb39ebacb8 100644 --- a/packages/backend/test-federation/test/notification.test.ts +++ b/packages/backend/test-federation/test/notification.test.ts @@ -1,14 +1,6 @@ import { deepStrictEqual, strictEqual } from 'assert'; import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, isFired, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; - -const [ - [, aAdminClient], - [, bAdminClient], -] = await Promise.all([ - fetchAdmin('a.test'), - fetchAdmin('b.test'), -]); +import { createAccount, isFired, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; describe('Notification', () => { let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; @@ -16,8 +8,8 @@ describe('Notification', () => { let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test', aAdminClient); - [bob, bobClient] = await createAccount('b.test', bAdminClient); + [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test'); + [bob, bobClient] = await createAccount('b.test'); [bobInAServer, aliceInBServer] = await Promise.all([ resolveRemoteUser('b.test', bob.id, aliceClient), diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index 1b02ee809e5d..53af68798203 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -2,13 +2,7 @@ import { strictEqual } from 'assert'; import * as Misskey from 'misskey-js'; import { createAccount, fetchAdmin, isFired, type Request, resolveRemoteUser, sleep } from './utils.js'; -const [ - [, aAdminClient], - [, bAdminClient], -] = await Promise.all([ - fetchAdmin('a.test'), - fetchAdmin('b.test'), -]); +const [, bAdminClient] = await fetchAdmin('b.test'); describe('Timeline', () => { let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; @@ -16,8 +10,8 @@ describe('Timeline', () => { let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test', aAdminClient); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test'); + [bob, bobClient, { username: bobUsername }] = await createAccount('b.test'); [bobInAServer, aliceInBServer] = await Promise.all([ resolveRemoteUser('b.test', bob.id, aliceClient), diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index 50e97f03edb3..154a73f85761 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -2,13 +2,7 @@ import assert, { rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; -const [ - [, aAdminClient], - [, bAdminClient], -] = await Promise.all([ - fetchAdmin('a.test'), - fetchAdmin('b.test'), -]); +const [, aAdminClient] = await fetchAdmin('a.test'); describe('User', () => { describe('Profile', () => { @@ -18,13 +12,13 @@ describe('User', () => { let aliceWatcherInBServerClient: Misskey.api.APIClient; beforeAll(async () => { - [alice] = await createAccount('a.test', aAdminClient); + [alice] = await createAccount('a.test'); [ [, aliceWatcherClient], [, aliceWatcherInBServerClient], ] = await Promise.all([ - createAccount('a.test', aAdminClient), - createAccount('b.test', bAdminClient), + createAccount('a.test'), + createAccount('b.test'), ]); }); @@ -57,8 +51,8 @@ describe('User', () => { let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test', aAdminClient); - [bob, bobClient] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test'); + [bob, bobClient] = await createAccount('b.test'); [bobInAServer, aliceInBServer] = await Promise.all([ resolveRemoteUser('b.test', bob.id, aliceClient), @@ -88,8 +82,8 @@ describe('User', () => { let aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test', aAdminClient); - [bob, bobClient] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test'); + [bob, bobClient] = await createAccount('b.test'); aliceInBServer = await resolveRemoteUser('a.test', alice.id, bobClient); await bobClient.request('following/create', { userId: aliceInBServer.id }); @@ -142,8 +136,8 @@ describe('User', () => { let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test', aAdminClient); - [bob, bobClient] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test'); + [bob, bobClient] = await createAccount('b.test'); [bobInAServer, aliceInBServer] = await Promise.all([ resolveRemoteUser('b.test', bob.id, aliceClient), @@ -204,8 +198,8 @@ describe('User', () => { let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test', aAdminClient); - [bob, bobClient] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test'); + [bob, bobClient] = await createAccount('b.test'); [bobInAServer, aliceInBServer] = await Promise.all([ resolveRemoteUser('b.test', bob.id, aliceClient), @@ -293,8 +287,8 @@ describe('User', () => { let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test', aAdminClient); - [bob, bobClient] = await createAccount('b.test', bAdminClient); + [alice, aliceClient] = await createAccount('a.test'); + [bob, bobClient] = await createAccount('b.test'); [bobInAServer, aliceInBServer] = await Promise.all([ resolveRemoteUser('b.test', bob.id, aliceClient), diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 442c9fd45a2b..1fce5cd5ca29 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -9,6 +9,18 @@ import { SwitchCaseResponseType } from 'misskey-js/api.types.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); +export const ADMIN_PARAMS = { username: 'admin', password: 'admin' }; +const adminCache = new Map(); + +let fetched = false; +if (!fetched) { + await Promise.all([ + fetchAdmin('a.test'), + fetchAdmin('b.test'), + ]); + fetched = true; +} + /** used for avoiding overload and some endpoints */ export type Request = ( endpoint: E, params: P, credential?: string | null @@ -16,13 +28,11 @@ export type Request = { return new Promise(resolve => setTimeout(resolve, ms)); } -export async function signin(host: Host, params: Misskey.entities.SigninRequest): Promise { +async function signin(host: Host, params: Misskey.entities.SigninRequest): Promise { // wait for a second to prevent hit rate limit await sleep(1000); // console.log(`Sign in to @${params.username}@${host} ...`); @@ -42,15 +52,13 @@ export async function signin(host: Host, params: Misskey.entities.SigninRequest) }) .catch(async err => { if (err.id === '22d05606-fbcf-421a-a2db-b32610dcfd1b') { - await sleep(Math.random() * 5000); + await sleep(Math.random() * 2000); return await signin(host, params); } throw err; }); } -const adminCache = new Map(); - async function createAdmin(host: Host): Promise { const client = new Misskey.api.APIClient({ origin: `https://${host}` }); return await client.request('admin/accounts/create', ADMIN_PARAMS).then(res => { @@ -78,8 +86,6 @@ async function createAdmin(host: Host): Promise { - await sleep(Math.random() * 5000); - const admin = adminCache.get(host) ?? await signin(host, ADMIN_PARAMS) .then(res => { adminCache.set(host, res); @@ -98,9 +104,10 @@ export async function fetchAdmin(host: Host): Promise<[Misskey.entities.SigninRe return [admin, new Misskey.api.APIClient({ origin: `https://${host}`, credential: admin.i })]; } -export async function createAccount(host: Host, adminClient: Misskey.api.APIClient): Promise<[Misskey.entities.SigninResponse, Misskey.api.APIClient, { username: string; password: string }]> { +export async function createAccount(host: Host): Promise<[Misskey.entities.SigninResponse, Misskey.api.APIClient, { username: string; password: string }]> { const username = crypto.randomUUID().replaceAll('-', '').substring(0, 20); const password = crypto.randomUUID().replaceAll('-', ''); + const [, adminClient] = await fetchAdmin(host); await adminClient.request('admin/accounts/create', { username, password }); // console.log(`Created an account: @${username}@${host}`); const signinRes = await signin(host, { username, password }); From 540d9dab374df412538350e0a3e59da4b7117d5c Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 23:15:32 +0900 Subject: [PATCH 064/111] perf: faster tests --- .../test-federation/test/block.test.ts | 20 +++++----- .../test-federation/test/emoji.test.ts | 6 +-- .../backend/test-federation/test/move.test.ts | 2 +- .../backend/test-federation/test/note.test.ts | 14 +++---- .../test-federation/test/timeline.test.ts | 8 ++-- .../backend/test-federation/test/user.test.ts | 38 +++++++++---------- .../backend/test-federation/test/utils.ts | 2 +- 7 files changed, 45 insertions(+), 45 deletions(-) diff --git a/packages/backend/test-federation/test/block.test.ts b/packages/backend/test-federation/test/block.test.ts index c9cf8421a005..0d689390adf5 100644 --- a/packages/backend/test-federation/test/block.test.ts +++ b/packages/backend/test-federation/test/block.test.ts @@ -20,7 +20,7 @@ describe('Block', () => { test('Cannot follow if blocked', async () => { await aliceClient.request('blocking/create', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); await rejects( async () => await bobClient.request('following/create', { userId: aliceInBServer.id }), (err: any) => { @@ -39,7 +39,7 @@ describe('Block', () => { test('Cannot follow even if unblocked', async () => { // unblock here await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); // TODO: why still being blocked? await rejects( @@ -53,10 +53,10 @@ describe('Block', () => { test.skip('Can follow if unblocked', async () => { await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); await bobClient.request('following/create', { userId: aliceInBServer.id }); - await sleep(1000); + await sleep(100); const following = await bobClient.request('users/following', { userId: bob.id }); strictEqual(following.length, 1); @@ -73,7 +73,7 @@ describe('Block', () => { }); await aliceClient.request('blocking/create', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); test('after block', async () => { const following = await bobClient.request('users/following', { userId: bob.id }); @@ -101,7 +101,7 @@ describe('Block', () => { test('Cannot reply if blocked', async () => { await aliceClient.request('blocking/create', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); @@ -116,7 +116,7 @@ describe('Block', () => { test('Can reply if unblocked', async () => { await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); @@ -143,7 +143,7 @@ describe('Block', () => { test('Cannot reaction if blocked', async () => { await aliceClient.request('blocking/create', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); @@ -160,7 +160,7 @@ describe('Block', () => { test('Cannot reaction even if unblocked', async () => { // unblock here await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); @@ -177,7 +177,7 @@ describe('Block', () => { test.skip('Can reaction if unblocked', async () => { await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); diff --git a/packages/backend/test-federation/test/emoji.test.ts b/packages/backend/test-federation/test/emoji.test.ts index 1a416a62a06a..03aefe0181a3 100644 --- a/packages/backend/test-federation/test/emoji.test.ts +++ b/packages/backend/test-federation/test/emoji.test.ts @@ -19,7 +19,7 @@ describe('Emoji', () => { ]); await bobClient.request('following/create', { userId: aliceInBServer.id }); - await sleep(1000); + await sleep(100); }); test('A server creates a custom emoji, and Bob can resolve it from Note', async () => { @@ -27,7 +27,7 @@ describe('Emoji', () => { const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); await aAdminClient.request('admin/emoji/add', { name, fileId: file.id }); const note = (await aliceClient.request('notes/create', { text: `I love :${name}:` })).createdNote; - await sleep(1000); + await sleep(100); const notes = await bobClient.request('notes/timeline', {}); strictEqual(notes.length, 1); @@ -44,7 +44,7 @@ describe('Emoji', () => { const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); await aAdminClient.request('admin/emoji/add', { name, fileId: file.id }); await aliceClient.request('i/update', { name: `:${name}:` }); - await sleep(1000); + await sleep(100); const renewedAliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); assert(name in renewedAliceInBServer.emojis); diff --git a/packages/backend/test-federation/test/move.test.ts b/packages/backend/test-federation/test/move.test.ts index e4cf30e24adc..93bf8589ffbd 100644 --- a/packages/backend/test-federation/test/move.test.ts +++ b/packages/backend/test-federation/test/move.test.ts @@ -28,7 +28,7 @@ describe('Move', () => { // Move @alice@a.test ==> @bob@b.test await bobClient.request('i/update', { alsoKnownAs: [`@${aliceUsername}@a.test`] }); await aliceClient.request('i/move', { moveToAccount: `@${bobUsername}@b.test` }); - await sleep(3000); + await sleep(100); }); test('Check from follower', async () => { diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index e4d72a616bda..0cbc3765bd8e 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -84,7 +84,7 @@ describe('Note', () => { ]); strictEqual(aliceInBServer.id, resolvedNote.userId); - await sleep(1000); + await sleep(100); const resolvedReplyedNote = await bobClient.request('notes/show', { noteId: resolvedNote.replyId }); strictEqual(resolvedReplyedNote.repliesCount, 1); @@ -148,14 +148,14 @@ describe('Note', () => { [, carolClient] = await createAccount('a.test'); await carolClient.request('following/create', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); }); test('Delete is derivered to followers', async () => { const note = (await bobClient.request('notes/create', { text: 'I\'m Bob.' })).createdNote; const noteInAServer = await resolveRemoteNote('b.test', note.id, carolClient); await bobClient.request('notes/delete', { noteId: note.id }); - await sleep(1000); + await sleep(100); await rejects( async () => await carolClient.request('notes/show', { noteId: noteInAServer.id }), @@ -173,7 +173,7 @@ describe('Note', () => { const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); const reaction = '😅'; await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction }); - await sleep(1000); + await sleep(100); const reactions = await aliceClient.request('notes/reactions', { noteId: note.id }); strictEqual(reactions.length, 1); @@ -194,7 +194,7 @@ describe('Note', () => { const note = (await bobClient.request('notes/create', { poll: { choices: ['inu', 'neko'] } })).createdNote; const noteInAServer = await resolveRemoteNote('b.test', note.id, carolClient); await carolClient.request('notes/polls/vote', { noteId: noteInAServer.id, choice: 0 }); - await sleep(1000); + await sleep(100); const noteAfterVote = await bobClient.request('notes/show', { noteId: note.id }); assert(noteAfterVote.poll != null); @@ -217,7 +217,7 @@ describe('Note', () => { ]); await bobRemoteFollowerClient.request('following/create', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); }); test('A vote in Bob\'s server is delivered to Bob\'s remote followers', async () => { @@ -225,7 +225,7 @@ describe('Note', () => { // NOTE: resolve before voting const noteInAServer = await resolveRemoteNote('b.test', note.id, bobRemoteFollowerClient); await localVoterClient.request('notes/polls/vote', { noteId: note.id, choice: 0 }); - await sleep(1000); + await sleep(100); const noteAfterVote = await bobRemoteFollowerClient.request('notes/show', { noteId: noteInAServer.id }); assert(noteAfterVote.poll != null); diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index 53af68798203..d2491f232160 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -19,7 +19,7 @@ describe('Timeline', () => { ]); await bobClient.request('following/create', { userId: aliceInBServer.id }); - await sleep(1000); + await sleep(100); }); async function postFromAlice(params?: Misskey.entities.NotesCreateRequest) { @@ -176,7 +176,7 @@ describe('Timeline', () => { beforeAll(async () => { list = await bobClient.request('users/lists/create', { name: 'Bob\'s List' }); await bobClient.request('users/lists/push', { listId: list.id, userId: aliceInBServer.id }); - await sleep(1000); + await sleep(100); }); describe('Check reception of remote followee\'s Note', () => { @@ -248,7 +248,7 @@ describe('Timeline', () => { displayOrder: 0, policies: {}, }); - await sleep(1000); + await sleep(100); }); describe('Check reception of remote followee\'s Note', () => { @@ -292,7 +292,7 @@ describe('Timeline', () => { withReplies: true, withFile: true, }); - await sleep(1000); + await sleep(100); }); describe('Check reception of remote followee\'s Note', () => { diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index 154a73f85761..bef696dbd0b5 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -66,10 +66,10 @@ describe('User', () => { test('Becoming a cat is sent to their followers', async () => { await bobClient.request('following/create', { userId: aliceInBServer.id }); - await sleep(1000); + await sleep(100); await aliceClient.request('i/update', { isCat: true }); - await sleep(1000); + await sleep(100); const res = await bobClient.request('users/show', { userId: aliceInBServer.id }); strictEqual(res.isCat, true); @@ -92,7 +92,7 @@ describe('User', () => { test('Pinning localOnly Note is not delivered', async () => { const note = (await aliceClient.request('notes/create', { text: 'a', localOnly: true })).createdNote; await aliceClient.request('i/pin', { noteId: note.id }); - await sleep(1000); + await sleep(100); const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); @@ -101,7 +101,7 @@ describe('User', () => { test('Pinning followers-only Note is not delivered', async () => { const note = (await aliceClient.request('notes/create', { text: 'a', visibility: 'followers' })).createdNote; await aliceClient.request('i/pin', { noteId: note.id }); - await sleep(1000); + await sleep(100); const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); @@ -112,7 +112,7 @@ describe('User', () => { test('Pinning normal Note is delivered', async () => { pinnedNote = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; await aliceClient.request('i/pin', { noteId: pinnedNote.id }); - await sleep(1000); + await sleep(100); const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 1); @@ -122,7 +122,7 @@ describe('User', () => { test('Unpinning normal Note is delivered', async () => { await aliceClient.request('i/unpin', { noteId: pinnedNote.id }); - await sleep(1000); + await sleep(100); const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); @@ -149,7 +149,7 @@ describe('User', () => { beforeAll(async () => { await aliceClient.request('following/create', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); }); test('Check consistency with `users/following` and `users/followers` endpoints', async () => { @@ -172,7 +172,7 @@ describe('User', () => { beforeAll(async () => { await aliceClient.request('following/delete', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); }); test('Check consistency with `users/following` and `users/followers` endpoints', async () => { @@ -213,7 +213,7 @@ describe('User', () => { describe('Bob sends follow request to Alice', () => { beforeAll(async () => { await bobClient.request('following/create', { userId: aliceInBServer.id }); - await sleep(1000); + await sleep(100); }); test('Alice should have a request', async () => { @@ -227,7 +227,7 @@ describe('User', () => { describe('Alice cancels it', () => { beforeAll(async () => { await bobClient.request('following/requests/cancel', { userId: aliceInBServer.id }); - await sleep(1000); + await sleep(100); }); test('Alice should have no requests', async () => { @@ -240,10 +240,10 @@ describe('User', () => { describe('Send follow request from Bob to Alice and reject', () => { beforeAll(async () => { await bobClient.request('following/create', { userId: aliceInBServer.id }); - await sleep(1000); + await sleep(100); await aliceClient.request('following/requests/reject', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); }); test('Bob should have no requests', async () => { @@ -265,10 +265,10 @@ describe('User', () => { describe('Send follow request from Bob to Alice and accept', () => { beforeAll(async () => { await bobClient.request('following/create', { userId: aliceInBServer.id }); - await sleep(1000); + await sleep(100); await aliceClient.request('following/requests/accept', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); }); test('Bob follows Alice', async () => { @@ -298,13 +298,13 @@ describe('User', () => { test('Bob follows Alice, and Alice gets suspended, there is no following relation, and Bob fails to follow again', async () => { await bobClient.request('following/create', { userId: aliceInBServer.id }); - await sleep(1000); + await sleep(100); const followers = await aliceClient.request('users/followers', { userId: alice.id }); strictEqual(followers.length, 1); // followed by Bob await aAdminClient.request('admin/suspend-user', { userId: alice.id }); - await sleep(1000); + await sleep(100); const following = await bobClient.request('users/following', { userId: bob.id }); strictEqual(following.length, 0); // no following relation @@ -320,7 +320,7 @@ describe('User', () => { test('Alice gets unsuspended, Bob succeeds in following Alice', async () => { await aAdminClient.request('admin/unsuspend-user', { userId: alice.id }); - await sleep(1000); + await sleep(100); const followers = await aliceClient.request('users/followers', { userId: alice.id }); strictEqual(followers.length, 1); // FIXME: followers are not deleted?? @@ -353,7 +353,7 @@ describe('User', () => { */ test('Alice can follow Bob', async () => { await aliceClient.request('following/create', { userId: bobInAServer.id }); - await sleep(1000); + await sleep(100); const bobFollowers = await bobClient.request('users/followers', { userId: bob.id }); strictEqual(bobFollowers.length, 1); // followed by Alice @@ -368,7 +368,7 @@ describe('User', () => { // Bob tries to follow Alice await bobClient.request('following/create', { userId: renewedAliceInBServer.id }); - await sleep(1000); + await sleep(100); const aliceFollowers = await aliceClient.request('users/followers', { userId: alice.id }); strictEqual(aliceFollowers.length, 1); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 1fce5cd5ca29..6ef1d829fdef 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -203,7 +203,7 @@ export async function isFired { stream.close(); resolve(false); - }, 3000); + }, 1000); await trigger().catch(err => { stream.close(); From fdde76f5dab221f8d3685334e2b292144b7214af Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 23 Sep 2024 23:50:38 +0900 Subject: [PATCH 065/111] test(drive): sensitive flag --- .../test-federation/test/drive.test.ts | 84 ++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index 80d42503b75c..3717a08f3abf 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -1,6 +1,6 @@ import assert, { strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, uploadFile } from './utils.js'; +import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, resolveRemoteUser, sleep, uploadFile } from './utils.js'; const [, bAdminClient] = await fetchAdmin('b.test'); @@ -87,4 +87,86 @@ describe('Drive', () => { }); }); }); + + describe('Sensitive flag', () => { + describe('isSensitive is federated in delivering to followers', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, aliceClient] = await createAccount('a.test'); + [bob, bobClient] = await createAccount('b.test'); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, aliceClient), + resolveRemoteUser('a.test', alice.id, bobClient), + ]); + + await bobClient.request('following/create', { userId: aliceInBServer.id }); + await sleep(100); + }); + + test('Alice uploads sensitive image and it is shown as sensitive from Bob', async () => { + const file = await uploadFile('a.test', '../../test/resources/192.jpg', alice.i); + await aliceClient.request('drive/files/update', { fileId: file.id, isSensitive: true }); + await aliceClient.request('notes/create', { text: 'sensitive', fileIds: [file.id] }); + await sleep(100); + + const notes = await bobClient.request('notes/timeline', {}); + strictEqual(notes.length, 1); + const noteInBServer = notes[0]; + assert(noteInBServer.files != null); + strictEqual(noteInBServer.files.length, 1); + strictEqual(noteInBServer.files[0].isSensitive, true); + }); + }); + + describe('isSensitive is federated in resolving', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + + beforeAll(async () => { + [alice, aliceClient] = await createAccount('a.test'); + [bob, bobClient] = await createAccount('b.test'); + }); + + test('Alice uploads sensitive image and it is shown as sensitive from Bob', async () => { + const file = await uploadFile('a.test', '../../test/resources/192.jpg', alice.i); + await aliceClient.request('drive/files/update', { fileId: file.id, isSensitive: true }); + const note = (await aliceClient.request('notes/create', { text: 'sensitive', fileIds: [file.id] })).createdNote; + + const noteInBServer = await resolveRemoteNote('a.test', note.id, bobClient); + assert(noteInBServer.files != null); + strictEqual(noteInBServer.files.length, 1); + strictEqual(noteInBServer.files[0].isSensitive, true); + }); + }); + + /** @see https://github.com/misskey-dev/misskey/issues/12208 */ + describe('isSensitive is federated in replying', () => { + let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; + let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + + beforeAll(async () => { + [alice, aliceClient] = await createAccount('a.test'); + [bob, bobClient] = await createAccount('b.test'); + }); + + test('Alice uploads sensitive image and it is shown as sensitive from Bob', async () => { + const bobNote = (await bobClient.request('notes/create', { text: 'I\'m Bob' })).createdNote; + + const file = await uploadFile('a.test', '../../test/resources/192.jpg', alice.i); + await aliceClient.request('drive/files/update', { fileId: file.id, isSensitive: true }); + const bobNoteInAServer = await resolveRemoteNote('b.test', bobNote.id, aliceClient); + const note = (await aliceClient.request('notes/create', { text: 'sensitive', fileIds: [file.id], replyId: bobNoteInAServer.id })).createdNote; + await sleep(1000); + + const noteInBServer = await resolveRemoteNote('a.test', note.id, bobClient); + assert(noteInBServer.files != null); + strictEqual(noteInBServer.files.length, 1); + strictEqual(noteInBServer.files[0].isSensitive, true); + }); + }); + }); }); From 38703a72b35a7b43bdcb1dc449a6745cd4c1b880 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Tue, 24 Sep 2024 00:57:46 +0900 Subject: [PATCH 066/111] test(emoji): add tests --- .../test-federation/test/emoji.test.ts | 71 +++++++++++++++++-- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/packages/backend/test-federation/test/emoji.test.ts b/packages/backend/test-federation/test/emoji.test.ts index 03aefe0181a3..bb73233e2280 100644 --- a/packages/backend/test-federation/test/emoji.test.ts +++ b/packages/backend/test-federation/test/emoji.test.ts @@ -1,4 +1,4 @@ -import assert, { strictEqual } from 'assert'; +import assert, { deepStrictEqual, strictEqual } from 'assert'; import * as Misskey from 'misskey-js'; import { createAccount, fetchAdmin, resolveRemoteUser, sleep, uploadFile } from './utils.js'; @@ -22,7 +22,7 @@ describe('Emoji', () => { await sleep(100); }); - test('A server creates a custom emoji, and Bob can resolve it from Note', async () => { + test('Custom emoji are delivered with Note delivery', async () => { const name = crypto.randomUUID().replaceAll('-', ''); const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); await aAdminClient.request('admin/emoji/add', { name, fileId: file.id }); @@ -30,24 +30,81 @@ describe('Emoji', () => { await sleep(100); const notes = await bobClient.request('notes/timeline', {}); - strictEqual(notes.length, 1); const noteInBServer = notes[0]; assert(note.text !== noteInBServer.text); // TODO: why? assert(noteInBServer.emojis != null); assert(name in noteInBServer.emojis); - assert(noteInBServer.emojis[name] === file.url); + strictEqual(noteInBServer.emojis[name], file.url); }); - test('A server creates a custom emoji, and Bob can resolve it from Profile', async () => { + test('Custom emoji are delivered with Reaction delivery', async () => { const name = crypto.randomUUID().replaceAll('-', ''); const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); await aAdminClient.request('admin/emoji/add', { name, fileId: file.id }); - await aliceClient.request('i/update', { name: `:${name}:` }); + const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + await sleep(100); + + await aliceClient.request('notes/reactions/create', { noteId: note.id, reaction: `:${name}:` }); + await sleep(100); + + const noteInBServer = (await bobClient.request('notes/timeline', {}))[0]; + deepStrictEqual(noteInBServer.reactions[`:${name}@a.test:`], 1); + deepStrictEqual(noteInBServer.reactionEmojis[`${name}@a.test`], file.url); + }); + + test('Custom emoji are delivered with Profile delivery', async () => { + const name = crypto.randomUUID().replaceAll('-', ''); + const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); + await aAdminClient.request('admin/emoji/add', { name, fileId: file.id }); + const renewedAlice = await aliceClient.request('i/update', { name: `:${name}:` }); await sleep(100); const renewedAliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); + strictEqual(renewedAliceInBServer.name, renewedAlice.name); assert(name in renewedAliceInBServer.emojis); - assert(renewedAliceInBServer.emojis[name] === file.url); + strictEqual(renewedAliceInBServer.emojis[name], file.url); + }); + + test('Local-only custom emoji aren\'t delivered with Note delivery', async () => { + const name = crypto.randomUUID().replaceAll('-', ''); + const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); + await aAdminClient.request('admin/emoji/add', { name, fileId: file.id, localOnly: true }); + const note = (await aliceClient.request('notes/create', { text: `I love :${name}:` })).createdNote; + await sleep(100); + + const notes = await bobClient.request('notes/timeline', {}); + const noteInBServer = notes[0]; + + assert(note.text !== noteInBServer.text); // TODO: why? + // deepStrictEqual(noteInBServer.emojis, {}); // TODO: this fails (why?) + deepStrictEqual({ ...noteInBServer.emojis }, {}); + }); + + test('Local-only custom emoji aren\'t delivered with Reaction delivery', async () => { + const name = crypto.randomUUID().replaceAll('-', ''); + const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); + await aAdminClient.request('admin/emoji/add', { name, fileId: file.id, localOnly: true }); + const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + await sleep(100); + + await aliceClient.request('notes/reactions/create', { noteId: note.id, reaction: `:${name}:` }); + await sleep(100); + + const noteInBServer = (await bobClient.request('notes/timeline', {}))[0]; + deepStrictEqual({ ...noteInBServer.reactions }, { '❤': 1 }); + deepStrictEqual({ ...noteInBServer.reactionEmojis }, {}); + }); + + test('Local-only custom emoji aren\'t delivered with Profile delivery', async () => { + const name = crypto.randomUUID().replaceAll('-', ''); + const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); + await aAdminClient.request('admin/emoji/add', { name, fileId: file.id, localOnly: true }); + const renewedAlice = await aliceClient.request('i/update', { name: `:${name}:` }); + await sleep(100); + + const renewedAliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); + strictEqual(renewedAliceInBServer.name, renewedAlice.name); + deepStrictEqual({ ...renewedAliceInBServer.emojis }, {}); }); }); From 0125197cb0eb5f8a9caa2827fa6dbddc28a33817 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Tue, 24 Sep 2024 01:16:48 +0900 Subject: [PATCH 067/111] chore: ignore .config/docker.env --- .../test-federation/.config/{docker.env => example.docker.env} | 1 + packages/backend/test-federation/.gitignore | 1 + packages/backend/test-federation/README.md | 1 + 3 files changed, 3 insertions(+) rename packages/backend/test-federation/.config/{docker.env => example.docker.env} (89%) diff --git a/packages/backend/test-federation/.config/docker.env b/packages/backend/test-federation/.config/example.docker.env similarity index 89% rename from packages/backend/test-federation/.config/docker.env rename to packages/backend/test-federation/.config/example.docker.env index b2a0177c84ca..a8af7cce49fb 100644 --- a/packages/backend/test-federation/.config/docker.env +++ b/packages/backend/test-federation/.config/example.docker.env @@ -2,3 +2,4 @@ NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/rootCA.crt POSTGRES_DB=misskey POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres +MK_VERBOSE=true diff --git a/packages/backend/test-federation/.gitignore b/packages/backend/test-federation/.gitignore index dd03bf2cab20..221c79104da1 100644 --- a/packages/backend/test-federation/.gitignore +++ b/packages/backend/test-federation/.gitignore @@ -1,3 +1,4 @@ certificates volumes .env +docker.env diff --git a/packages/backend/test-federation/README.md b/packages/backend/test-federation/README.md index 9bc1e87b2561..bf0a8e88abd1 100644 --- a/packages/backend/test-federation/README.md +++ b/packages/backend/test-federation/README.md @@ -4,6 +4,7 @@ Test federation between two Misskey servers: `a.test` and `b.test`. First, you need to start server by executing following commands: ```sh cp ./.env.example ./.env +cp ./.config/example.docker.env ./.config/docker.env bash ./generate_certificates.sh docker compose up --scale tester=0 ``` From cb131d36272a705c44e3ddd8a3139e48eec9b6a8 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Tue, 24 Sep 2024 01:24:05 +0900 Subject: [PATCH 068/111] chore: hard-coded tester IP address --- packages/backend/test-federation/.env.example | 1 - packages/backend/test-federation/README.md | 1 - packages/backend/test-federation/compose.override.yaml | 2 +- packages/backend/test-federation/compose.yml | 2 -- packages/backend/test-federation/daemon.ts | 4 +++- 5 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 packages/backend/test-federation/.env.example diff --git a/packages/backend/test-federation/.env.example b/packages/backend/test-federation/.env.example deleted file mode 100644 index 97ec4d359404..000000000000 --- a/packages/backend/test-federation/.env.example +++ /dev/null @@ -1 +0,0 @@ -TESTER_IP_ADDRESS=172.20.1.1 diff --git a/packages/backend/test-federation/README.md b/packages/backend/test-federation/README.md index bf0a8e88abd1..57a34a6f4106 100644 --- a/packages/backend/test-federation/README.md +++ b/packages/backend/test-federation/README.md @@ -3,7 +3,6 @@ Test federation between two Misskey servers: `a.test` and `b.test`. First, you need to start server by executing following commands: ```sh -cp ./.env.example ./.env cp ./.config/example.docker.env ./.config/docker.env bash ./generate_certificates.sh docker compose up --scale tester=0 diff --git a/packages/backend/test-federation/compose.override.yaml b/packages/backend/test-federation/compose.override.yaml index 4de162f677bf..4cd38de93640 100644 --- a/packages/backend/test-federation/compose.override.yaml +++ b/packages/backend/test-federation/compose.override.yaml @@ -3,7 +3,7 @@ services: networks: external_network: internal_network: - ipv4_address: $TESTER_IP_ADDRESS + ipv4_address: 172.20.1.1 volumes: - type: volume source: node_modules_dev diff --git a/packages/backend/test-federation/compose.yml b/packages/backend/test-federation/compose.yml index 5ae4582a052b..2019d6b20307 100644 --- a/packages/backend/test-federation/compose.yml +++ b/packages/backend/test-federation/compose.yml @@ -75,8 +75,6 @@ services: condition: service_healthy misskey.b.test: condition: service_healthy - environment: - - TESTER_IP_ADDRESS=$TESTER_IP_ADDRESS volumes: - type: bind source: ../package.json diff --git a/packages/backend/test-federation/daemon.ts b/packages/backend/test-federation/daemon.ts index f6dfa1699da7..46b6963c79ac 100644 --- a/packages/backend/test-federation/daemon.ts +++ b/packages/backend/test-federation/daemon.ts @@ -1,6 +1,8 @@ import IPCIDR from 'ip-cidr'; import { Redis } from 'ioredis'; +const TESTER_IP_ADDRESS = '172.20.1.1'; + /** * This should be same as {@link file://./../src/misc/get-ip-hash.ts}. */ @@ -13,7 +15,7 @@ function getIpHash(ip: string) { * This prevents hitting rate limit when login. */ export async function purgeLimit(host: string, client: Redis) { - const ipHash = getIpHash(process.env.TESTER_IP_ADDRESS!); + const ipHash = getIpHash(TESTER_IP_ADDRESS); const key = `${host}:limit:${ipHash}:signin`; const res = await client.zrange(key, 0, -1); if (res.length !== 0) { From 8c6df97f8a426d51abc0ff3e4c24e71e2af1c5c6 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Tue, 24 Sep 2024 01:30:33 +0900 Subject: [PATCH 069/111] test(emoji): custom emoji are surrounded by zero width space --- packages/backend/test-federation/test/emoji.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/backend/test-federation/test/emoji.test.ts b/packages/backend/test-federation/test/emoji.test.ts index bb73233e2280..d9542db2ac64 100644 --- a/packages/backend/test-federation/test/emoji.test.ts +++ b/packages/backend/test-federation/test/emoji.test.ts @@ -26,13 +26,13 @@ describe('Emoji', () => { const name = crypto.randomUUID().replaceAll('-', ''); const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); await aAdminClient.request('admin/emoji/add', { name, fileId: file.id }); - const note = (await aliceClient.request('notes/create', { text: `I love :${name}:` })).createdNote; + await aliceClient.request('notes/create', { text: `I love :${name}:` }); await sleep(100); const notes = await bobClient.request('notes/timeline', {}); const noteInBServer = notes[0]; - assert(note.text !== noteInBServer.text); // TODO: why? + strictEqual(noteInBServer.text, `I love \u200b:${name}:\u200b`); assert(noteInBServer.emojis != null); assert(name in noteInBServer.emojis); strictEqual(noteInBServer.emojis[name], file.url); @@ -70,13 +70,13 @@ describe('Emoji', () => { const name = crypto.randomUUID().replaceAll('-', ''); const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); await aAdminClient.request('admin/emoji/add', { name, fileId: file.id, localOnly: true }); - const note = (await aliceClient.request('notes/create', { text: `I love :${name}:` })).createdNote; + await aliceClient.request('notes/create', { text: `I love :${name}:` }); await sleep(100); const notes = await bobClient.request('notes/timeline', {}); const noteInBServer = notes[0]; - assert(note.text !== noteInBServer.text); // TODO: why? + strictEqual(noteInBServer.text, `I love \u200b:${name}:\u200b`); // deepStrictEqual(noteInBServer.emojis, {}); // TODO: this fails (why?) deepStrictEqual({ ...noteInBServer.emojis }, {}); }); From 03ad31e98dc8ae149034690c31a2f4b6cd69b28f Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Tue, 24 Sep 2024 17:03:15 +0900 Subject: [PATCH 070/111] refactor: client and username as property --- .../test-federation/test/block.test.ts | 115 +++++------ .../test-federation/test/drive.test.ts | 75 +++---- .../test-federation/test/emoji.test.ts | 59 +++--- .../backend/test-federation/test/move.test.ts | 38 ++-- .../backend/test-federation/test/note.test.ts | 90 ++++----- .../test-federation/test/notification.test.ts | 51 ++--- .../test-federation/test/timeline.test.ts | 39 ++-- .../backend/test-federation/test/user.test.ts | 183 +++++++++--------- .../backend/test-federation/test/utils.ts | 39 ++-- 9 files changed, 358 insertions(+), 331 deletions(-) diff --git a/packages/backend/test-federation/test/block.test.ts b/packages/backend/test-federation/test/block.test.ts index 0d689390adf5..81625c8b27d6 100644 --- a/packages/backend/test-federation/test/block.test.ts +++ b/packages/backend/test-federation/test/block.test.ts @@ -1,49 +1,50 @@ import { deepStrictEqual, rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; +import { createAccount, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; describe('Block', () => { describe('Check follow', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let alice: LoginUser, bob: LoginUser; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test'); - [bob, bobClient] = await createAccount('b.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, aliceClient), - resolveRemoteUser('a.test', alice.id, bobClient), + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), ]); }); test('Cannot follow if blocked', async () => { - await aliceClient.request('blocking/create', { userId: bobInAServer.id }); + await alice.client.request('blocking/create', { userId: bobInAServer.id }); await sleep(100); await rejects( - async () => await bobClient.request('following/create', { userId: aliceInBServer.id }), + async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), (err: any) => { strictEqual(err.code, 'BLOCKED'); return true; }, ); - const following = await bobClient.request('users/following', { userId: bob.id }); + const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 0); - const followers = await aliceClient.request('users/followers', { userId: alice.id }); + const followers = await alice.client.request('users/followers', { userId: alice.id }); strictEqual(followers.length, 0); }); // FIXME: this is invalid case test('Cannot follow even if unblocked', async () => { // unblock here - await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); + await alice.client.request('blocking/delete', { userId: bobInAServer.id }); await sleep(100); // TODO: why still being blocked? await rejects( - async () => await bobClient.request('following/create', { userId: aliceInBServer.id }), + async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), (err: any) => { strictEqual(err.code, 'BLOCKED'); return true; @@ -52,61 +53,62 @@ describe('Block', () => { }); test.skip('Can follow if unblocked', async () => { - await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); + await alice.client.request('blocking/delete', { userId: bobInAServer.id }); await sleep(100); - await bobClient.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInBServer.id }); await sleep(100); - const following = await bobClient.request('users/following', { userId: bob.id }); + const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 1); - const followers = await aliceClient.request('users/followers', { userId: alice.id }); + const followers = await alice.client.request('users/followers', { userId: alice.id }); strictEqual(followers.length, 1); }); test.skip('Remove follower when block them', async () => { test('before block', async () => { - const following = await bobClient.request('users/following', { userId: bob.id }); + const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 1); - const followers = await aliceClient.request('users/followers', { userId: alice.id }); + const followers = await alice.client.request('users/followers', { userId: alice.id }); strictEqual(followers.length, 1); }); - await aliceClient.request('blocking/create', { userId: bobInAServer.id }); + await alice.client.request('blocking/create', { userId: bobInAServer.id }); await sleep(100); test('after block', async () => { - const following = await bobClient.request('users/following', { userId: bob.id }); + const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 0); - const followers = await aliceClient.request('users/followers', { userId: alice.id }); + const followers = await alice.client.request('users/followers', { userId: alice.id }); strictEqual(followers.length, 0); }); }); }); describe('Check reply', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let alice: LoginUser, bob: LoginUser; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test'); - [bob, bobClient] = await createAccount('b.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, aliceClient), - resolveRemoteUser('a.test', alice.id, bobClient), + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), ]); }); test('Cannot reply if blocked', async () => { - await aliceClient.request('blocking/create', { userId: bobInAServer.id }); + await alice.client.request('blocking/create', { userId: bobInAServer.id }); await sleep(100); - const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); await rejects( - async () => await bobClient.request('notes/create', { text: 'b', replyId: resolvedNote.id }), + async () => await bob.client.request('notes/create', { text: 'b', replyId: resolvedNote.id }), (err: any) => { strictEqual(err.code, 'YOU_HAVE_BEEN_BLOCKED'); return true; @@ -115,40 +117,41 @@ describe('Block', () => { }); test('Can reply if unblocked', async () => { - await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); + await alice.client.request('blocking/delete', { userId: bobInAServer.id }); await sleep(100); - const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); - const reply = (await bobClient.request('notes/create', { text: 'b', replyId: resolvedNote.id })).createdNote; + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); + const reply = (await bob.client.request('notes/create', { text: 'b', replyId: resolvedNote.id })).createdNote; - await resolveRemoteNote('b.test', reply.id, aliceClient); + await resolveRemoteNote('b.test', reply.id, alice); }); }); describe('Check reaction', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let alice: LoginUser, bob: LoginUser; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test'); - [bob, bobClient] = await createAccount('b.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, aliceClient), - resolveRemoteUser('a.test', alice.id, bobClient), + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), ]); }); test('Cannot reaction if blocked', async () => { - await aliceClient.request('blocking/create', { userId: bobInAServer.id }); + await alice.client.request('blocking/create', { userId: bobInAServer.id }); await sleep(100); - const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); await rejects( - async () => await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: '😅' }), + async () => await bob.client.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: '😅' }), (err: any) => { strictEqual(err.code, 'YOU_HAVE_BEEN_BLOCKED'); return true; @@ -159,15 +162,15 @@ describe('Block', () => { // FIXME: this is invalid case test('Cannot reaction even if unblocked', async () => { // unblock here - await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); + await alice.client.request('blocking/delete', { userId: bobInAServer.id }); await sleep(100); - const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); // TODO: why still being blocked? await rejects( - async () => await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: '😅' }), + async () => await bob.client.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: '😅' }), (err: any) => { strictEqual(err.code, 'YOU_HAVE_BEEN_BLOCKED'); return true; @@ -176,14 +179,14 @@ describe('Block', () => { }); test.skip('Can reaction if unblocked', async () => { - await aliceClient.request('blocking/delete', { userId: bobInAServer.id }); + await alice.client.request('blocking/delete', { userId: bobInAServer.id }); await sleep(100); - const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); - await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: '😅' }); + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); + await bob.client.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: '😅' }); - const _note = await aliceClient.request('notes/show', { noteId: note.id }); + const _note = await alice.client.request('notes/show', { noteId: note.id }); deepStrictEqual(_note.reactions, { '😅': 1 }); }); }); diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index 3717a08f3abf..a0f3f10f84ba 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -1,15 +1,15 @@ import assert, { strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, resolveRemoteUser, sleep, uploadFile } from './utils.js'; +import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep, uploadFile } from './utils.js'; -const [, bAdminClient] = await fetchAdmin('b.test'); +const bAdmin = await fetchAdmin('b.test'); describe('Drive', () => { describe('Upload image in a.test and resolve from b.test', () => { - let uploader: Misskey.entities.SigninResponse, uploaderClient: Misskey.api.APIClient; + let uploader: LoginUser; beforeAll(async () => { - [uploader, uploaderClient] = await createAccount('a.test'); + uploader = await createAccount('a.test'); }); let image: Misskey.entities.DriveFile, imageInBServer: Misskey.entities.DriveFile; @@ -17,8 +17,8 @@ describe('Drive', () => { describe('Upload', () => { beforeAll(async () => { image = await uploadFile('a.test', '../../test/resources/192.jpg', uploader.i); - const noteWithImage = (await uploaderClient.request('notes/create', { fileIds: [image.id] })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', noteWithImage.id, bAdminClient); + const noteWithImage = (await uploader.client.request('notes/create', { fileIds: [image.id] })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', noteWithImage.id, bAdmin); assert(noteInBServer.files != null); strictEqual(noteInBServer.files.length, 1); imageInBServer = noteInBServer.files[0]; @@ -43,13 +43,13 @@ describe('Drive', () => { describe('Update', () => { beforeAll(async () => { - updatedImage = await uploaderClient.request('drive/files/update', { + updatedImage = await uploader.client.request('drive/files/update', { fileId: image.id, name: 'updated_192.jpg', isSensitive: true, }); - updatedImageInBServer = await bAdminClient.request('drive/files/show', { + updatedImageInBServer = await bAdmin.client.request('drive/files/show', { fileId: imageInBServer.id, }); }); @@ -70,8 +70,8 @@ describe('Drive', () => { describe('Re-update with attaching to Note', () => { beforeAll(async () => { - const noteWithUpdatedImage = (await uploaderClient.request('notes/create', { fileIds: [updatedImage.id] })).createdNote; - const noteWithUpdatedImageInBServer = await resolveRemoteNote('a.test', noteWithUpdatedImage.id, bAdminClient); + const noteWithUpdatedImage = (await uploader.client.request('notes/create', { fileIds: [updatedImage.id] })).createdNote; + const noteWithUpdatedImageInBServer = await resolveRemoteNote('a.test', noteWithUpdatedImage.id, bAdmin); assert(noteWithUpdatedImageInBServer.files != null); strictEqual(noteWithUpdatedImageInBServer.files.length, 1); reupdatedImageInBServer = noteWithUpdatedImageInBServer.files[0]; @@ -90,30 +90,31 @@ describe('Drive', () => { describe('Sensitive flag', () => { describe('isSensitive is federated in delivering to followers', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let alice: LoginUser, bob: LoginUser; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test'); - [bob, bobClient] = await createAccount('b.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, aliceClient), - resolveRemoteUser('a.test', alice.id, bobClient), + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), ]); - await bobClient.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInBServer.id }); await sleep(100); }); test('Alice uploads sensitive image and it is shown as sensitive from Bob', async () => { const file = await uploadFile('a.test', '../../test/resources/192.jpg', alice.i); - await aliceClient.request('drive/files/update', { fileId: file.id, isSensitive: true }); - await aliceClient.request('notes/create', { text: 'sensitive', fileIds: [file.id] }); + await alice.client.request('drive/files/update', { fileId: file.id, isSensitive: true }); + await alice.client.request('notes/create', { text: 'sensitive', fileIds: [file.id] }); await sleep(100); - const notes = await bobClient.request('notes/timeline', {}); + const notes = await bob.client.request('notes/timeline', {}); strictEqual(notes.length, 1); const noteInBServer = notes[0]; assert(noteInBServer.files != null); @@ -123,20 +124,21 @@ describe('Drive', () => { }); describe('isSensitive is federated in resolving', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let alice: LoginUser, bob: LoginUser; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test'); - [bob, bobClient] = await createAccount('b.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); }); test('Alice uploads sensitive image and it is shown as sensitive from Bob', async () => { const file = await uploadFile('a.test', '../../test/resources/192.jpg', alice.i); - await aliceClient.request('drive/files/update', { fileId: file.id, isSensitive: true }); - const note = (await aliceClient.request('notes/create', { text: 'sensitive', fileIds: [file.id] })).createdNote; + await alice.client.request('drive/files/update', { fileId: file.id, isSensitive: true }); + const note = (await alice.client.request('notes/create', { text: 'sensitive', fileIds: [file.id] })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bobClient); + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); assert(noteInBServer.files != null); strictEqual(noteInBServer.files.length, 1); strictEqual(noteInBServer.files[0].isSensitive, true); @@ -145,24 +147,25 @@ describe('Drive', () => { /** @see https://github.com/misskey-dev/misskey/issues/12208 */ describe('isSensitive is federated in replying', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let alice: LoginUser, bob: LoginUser; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test'); - [bob, bobClient] = await createAccount('b.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); }); test('Alice uploads sensitive image and it is shown as sensitive from Bob', async () => { - const bobNote = (await bobClient.request('notes/create', { text: 'I\'m Bob' })).createdNote; + const bobNote = (await bob.client.request('notes/create', { text: 'I\'m Bob' })).createdNote; const file = await uploadFile('a.test', '../../test/resources/192.jpg', alice.i); - await aliceClient.request('drive/files/update', { fileId: file.id, isSensitive: true }); - const bobNoteInAServer = await resolveRemoteNote('b.test', bobNote.id, aliceClient); - const note = (await aliceClient.request('notes/create', { text: 'sensitive', fileIds: [file.id], replyId: bobNoteInAServer.id })).createdNote; + await alice.client.request('drive/files/update', { fileId: file.id, isSensitive: true }); + const bobNoteInAServer = await resolveRemoteNote('b.test', bobNote.id, alice); + const note = (await alice.client.request('notes/create', { text: 'sensitive', fileIds: [file.id], replyId: bobNoteInAServer.id })).createdNote; await sleep(1000); - const noteInBServer = await resolveRemoteNote('a.test', note.id, bobClient); + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); assert(noteInBServer.files != null); strictEqual(noteInBServer.files.length, 1); strictEqual(noteInBServer.files[0].isSensitive, true); diff --git a/packages/backend/test-federation/test/emoji.test.ts b/packages/backend/test-federation/test/emoji.test.ts index d9542db2ac64..9d363899dec0 100644 --- a/packages/backend/test-federation/test/emoji.test.ts +++ b/packages/backend/test-federation/test/emoji.test.ts @@ -1,35 +1,36 @@ import assert, { deepStrictEqual, strictEqual } from 'assert'; import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, resolveRemoteUser, sleep, uploadFile } from './utils.js'; +import { createAccount, fetchAdmin, type LoginUser, resolveRemoteUser, sleep, uploadFile } from './utils.js'; -const [aAdmin, aAdminClient] = await fetchAdmin('a.test'); +const aAdmin = await fetchAdmin('a.test'); describe('Emoji', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let alice: LoginUser, bob: LoginUser; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test'); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, aliceClient), - resolveRemoteUser('a.test', alice.id, bobClient), + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), ]); - await bobClient.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInBServer.id }); await sleep(100); }); test('Custom emoji are delivered with Note delivery', async () => { const name = crypto.randomUUID().replaceAll('-', ''); const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); - await aAdminClient.request('admin/emoji/add', { name, fileId: file.id }); - await aliceClient.request('notes/create', { text: `I love :${name}:` }); + await aAdmin.client.request('admin/emoji/add', { name, fileId: file.id }); + await alice.client.request('notes/create', { text: `I love :${name}:` }); await sleep(100); - const notes = await bobClient.request('notes/timeline', {}); + const notes = await bob.client.request('notes/timeline', {}); const noteInBServer = notes[0]; strictEqual(noteInBServer.text, `I love \u200b:${name}:\u200b`); @@ -41,14 +42,14 @@ describe('Emoji', () => { test('Custom emoji are delivered with Reaction delivery', async () => { const name = crypto.randomUUID().replaceAll('-', ''); const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); - await aAdminClient.request('admin/emoji/add', { name, fileId: file.id }); - const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + await aAdmin.client.request('admin/emoji/add', { name, fileId: file.id }); + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; await sleep(100); - await aliceClient.request('notes/reactions/create', { noteId: note.id, reaction: `:${name}:` }); + await alice.client.request('notes/reactions/create', { noteId: note.id, reaction: `:${name}:` }); await sleep(100); - const noteInBServer = (await bobClient.request('notes/timeline', {}))[0]; + const noteInBServer = (await bob.client.request('notes/timeline', {}))[0]; deepStrictEqual(noteInBServer.reactions[`:${name}@a.test:`], 1); deepStrictEqual(noteInBServer.reactionEmojis[`${name}@a.test`], file.url); }); @@ -56,11 +57,11 @@ describe('Emoji', () => { test('Custom emoji are delivered with Profile delivery', async () => { const name = crypto.randomUUID().replaceAll('-', ''); const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); - await aAdminClient.request('admin/emoji/add', { name, fileId: file.id }); - const renewedAlice = await aliceClient.request('i/update', { name: `:${name}:` }); + await aAdmin.client.request('admin/emoji/add', { name, fileId: file.id }); + const renewedAlice = await alice.client.request('i/update', { name: `:${name}:` }); await sleep(100); - const renewedAliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); + const renewedAliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(renewedAliceInBServer.name, renewedAlice.name); assert(name in renewedAliceInBServer.emojis); strictEqual(renewedAliceInBServer.emojis[name], file.url); @@ -69,11 +70,11 @@ describe('Emoji', () => { test('Local-only custom emoji aren\'t delivered with Note delivery', async () => { const name = crypto.randomUUID().replaceAll('-', ''); const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); - await aAdminClient.request('admin/emoji/add', { name, fileId: file.id, localOnly: true }); - await aliceClient.request('notes/create', { text: `I love :${name}:` }); + await aAdmin.client.request('admin/emoji/add', { name, fileId: file.id, localOnly: true }); + await alice.client.request('notes/create', { text: `I love :${name}:` }); await sleep(100); - const notes = await bobClient.request('notes/timeline', {}); + const notes = await bob.client.request('notes/timeline', {}); const noteInBServer = notes[0]; strictEqual(noteInBServer.text, `I love \u200b:${name}:\u200b`); @@ -84,14 +85,14 @@ describe('Emoji', () => { test('Local-only custom emoji aren\'t delivered with Reaction delivery', async () => { const name = crypto.randomUUID().replaceAll('-', ''); const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); - await aAdminClient.request('admin/emoji/add', { name, fileId: file.id, localOnly: true }); - const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; + await aAdmin.client.request('admin/emoji/add', { name, fileId: file.id, localOnly: true }); + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; await sleep(100); - await aliceClient.request('notes/reactions/create', { noteId: note.id, reaction: `:${name}:` }); + await alice.client.request('notes/reactions/create', { noteId: note.id, reaction: `:${name}:` }); await sleep(100); - const noteInBServer = (await bobClient.request('notes/timeline', {}))[0]; + const noteInBServer = (await bob.client.request('notes/timeline', {}))[0]; deepStrictEqual({ ...noteInBServer.reactions }, { '❤': 1 }); deepStrictEqual({ ...noteInBServer.reactionEmojis }, {}); }); @@ -99,11 +100,11 @@ describe('Emoji', () => { test('Local-only custom emoji aren\'t delivered with Profile delivery', async () => { const name = crypto.randomUUID().replaceAll('-', ''); const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); - await aAdminClient.request('admin/emoji/add', { name, fileId: file.id, localOnly: true }); - const renewedAlice = await aliceClient.request('i/update', { name: `:${name}:` }); + await aAdmin.client.request('admin/emoji/add', { name, fileId: file.id, localOnly: true }); + const renewedAlice = await alice.client.request('i/update', { name: `:${name}:` }); await sleep(100); - const renewedAliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); + const renewedAliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(renewedAliceInBServer.name, renewedAlice.name); deepStrictEqual({ ...renewedAliceInBServer.emojis }, {}); }); diff --git a/packages/backend/test-federation/test/move.test.ts b/packages/backend/test-federation/test/move.test.ts index 93bf8589ffbd..8544e305e548 100644 --- a/packages/backend/test-federation/test/move.test.ts +++ b/packages/backend/test-federation/test/move.test.ts @@ -1,51 +1,53 @@ import assert, { strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, sleep } from './utils.js'; +import { createAccount, type LoginUser, sleep } from './utils.js'; describe('Move', () => { test('Minimum move', async () => { - const [, aliceClient, { username: aliceUsername }] = await createAccount('a.test'); - const [, bobClient, { username: bobUsername }] = await createAccount('b.test'); + const [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); - await bobClient.request('i/update', { alsoKnownAs: [`@${aliceUsername}@a.test`] }); - await aliceClient.request('i/move', { moveToAccount: `@${bobUsername}@b.test` }); + await bob.client.request('i/update', { alsoKnownAs: [`@${alice.username}@a.test`] }); + await alice.client.request('i/move', { moveToAccount: `@${bob.username}@b.test` }); }); /** @see https://github.com/misskey-dev/misskey/issues/11320 */ describe('Following relation is transferred after move', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; - let carol: Misskey.entities.SigninResponse, carolClient: Misskey.api.APIClient, carolUsername: string; + let alice: LoginUser, bob: LoginUser, carol: LoginUser; beforeAll(async () => { - [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test'); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test'); - [carol, carolClient, { username: carolUsername }] = await createAccount('a.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + carol = await createAccount('a.test'); // Follow @carol@a.test ==> @alice@a.test - await carolClient.request('following/create', { userId: alice.id }); + await carol.client.request('following/create', { userId: alice.id }); // Move @alice@a.test ==> @bob@b.test - await bobClient.request('i/update', { alsoKnownAs: [`@${aliceUsername}@a.test`] }); - await aliceClient.request('i/move', { moveToAccount: `@${bobUsername}@b.test` }); + await bob.client.request('i/update', { alsoKnownAs: [`@${alice.username}@a.test`] }); + await alice.client.request('i/move', { moveToAccount: `@${bob.username}@b.test` }); await sleep(100); }); test('Check from follower', async () => { - const following = await carolClient.request('users/following', { userId: carol.id }); + const following = await carol.client.request('users/following', { userId: carol.id }); strictEqual(following.length, 2); const followees = following.map(({ followee }) => followee); assert(followees.every(followee => followee != null)); assert(followees.some(({ id, url }) => id === alice.id && url === null)); - assert(followees.some(({ url }) => url === `https://b.test/@${bobUsername}`)); + assert(followees.some(({ url }) => url === `https://b.test/@${bob.username}`)); }); test('Check from followee', async () => { - const followers = await bobClient.request('users/followers', { userId: bob.id }); + const followers = await bob.client.request('users/followers', { userId: bob.id }); strictEqual(followers.length, 1); const follower = followers[0].follower; assert(follower != null); - strictEqual(follower.url, `https://a.test/@${carolUsername}`); + strictEqual(follower.url, `https://a.test/@${carol.username}`); }); }); }); diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index 0cbc3765bd8e..1bc85f212ff5 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -1,26 +1,27 @@ import assert, { rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, deepStrictEqualWithExcludedFields, resolveRemoteNote, resolveRemoteUser, sleep, uploadFile } from './utils.js'; +import { createAccount, deepStrictEqualWithExcludedFields, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep, uploadFile } from './utils.js'; describe('Note', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let alice: LoginUser, bob: LoginUser; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test'); - [bob, bobClient] = await createAccount('b.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, aliceClient), - resolveRemoteUser('a.test', alice.id, bobClient), + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), ]); }); describe('Note content', () => { test('Consistency of Public Note', async () => { const image = await uploadFile('a.test', '../../test/resources/192.jpg', alice.i); - const note = (await aliceClient.request('notes/create', { + const note = (await alice.client.request('notes/create', { text: 'I am Alice!', fileIds: [image.id], poll: { @@ -30,7 +31,7 @@ describe('Note', () => { }, })).createdNote; - const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); + const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); deepStrictEqualWithExcludedFields(note, resolvedNote, [ 'id', 'emojis', @@ -47,18 +48,18 @@ describe('Note', () => { }); test('Consistency of reply', async () => { - const _replyedNote = (await aliceClient.request('notes/create', { + const _replyedNote = (await alice.client.request('notes/create', { text: 'a', })).createdNote; - const note = (await aliceClient.request('notes/create', { + const note = (await alice.client.request('notes/create', { text: 'b', replyId: _replyedNote.id, })).createdNote; // NOTE: the repliedCount is incremented, so fetch again - const replyedNote = await aliceClient.request('notes/show', { noteId: _replyedNote.id }); + const replyedNote = await alice.client.request('notes/show', { noteId: _replyedNote.id }); strictEqual(replyedNote.repliesCount, 1); - const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); + const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); deepStrictEqualWithExcludedFields(note, resolvedNote, [ 'id', 'emojis', @@ -86,21 +87,21 @@ describe('Note', () => { await sleep(100); - const resolvedReplyedNote = await bobClient.request('notes/show', { noteId: resolvedNote.replyId }); + const resolvedReplyedNote = await bob.client.request('notes/show', { noteId: resolvedNote.replyId }); strictEqual(resolvedReplyedNote.repliesCount, 1); }); test('Consistency of Renote', async () => { // NOTE: the renoteCount is not incremented, so no need to fetch again - const renotedNote = (await aliceClient.request('notes/create', { + const renotedNote = (await alice.client.request('notes/create', { text: 'a', })).createdNote; - const note = (await aliceClient.request('notes/create', { + const note = (await alice.client.request('notes/create', { text: 'b', renoteId: renotedNote.id, })).createdNote; - const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); + const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); deepStrictEqualWithExcludedFields(note, resolvedNote, [ 'id', 'emojis', @@ -126,9 +127,9 @@ describe('Note', () => { describe('Other props', () => { test('localOnly', async () => { - const note = (await aliceClient.request('notes/create', { text: 'a', localOnly: true })).createdNote; + const note = (await alice.client.request('notes/create', { text: 'a', localOnly: true })).createdNote; rejects( - async () => await bobClient.request('ap/show', { uri: `https://a.test/notes/${note.id}` }), + async () => await bob.client.request('ap/show', { uri: `https://a.test/notes/${note.id}` }), (err: any) => { /** * FIXME: this error is not handled @@ -142,23 +143,23 @@ describe('Note', () => { }); describe('Deletion', () => { - let carolClient: Misskey.api.APIClient; + let carol: LoginUser; beforeAll(async () => { - [, carolClient] = await createAccount('a.test'); + carol = await createAccount('a.test'); - await carolClient.request('following/create', { userId: bobInAServer.id }); + await carol.client.request('following/create', { userId: bobInAServer.id }); await sleep(100); }); test('Delete is derivered to followers', async () => { - const note = (await bobClient.request('notes/create', { text: 'I\'m Bob.' })).createdNote; - const noteInAServer = await resolveRemoteNote('b.test', note.id, carolClient); - await bobClient.request('notes/delete', { noteId: note.id }); + const note = (await bob.client.request('notes/create', { text: 'I\'m Bob.' })).createdNote; + const noteInAServer = await resolveRemoteNote('b.test', note.id, carol); + await bob.client.request('notes/delete', { noteId: note.id }); await sleep(100); await rejects( - async () => await carolClient.request('notes/show', { noteId: noteInAServer.id }), + async () => await carol.client.request('notes/show', { noteId: noteInAServer.id }), (err: any) => { strictEqual(err.code, 'NO_SUCH_NOTE'); return true; @@ -169,13 +170,13 @@ describe('Note', () => { describe('Reaction', () => { test('Consistency of reaction', async () => { - const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote('a.test', note.id, bobClient); + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); const reaction = '😅'; - await bobClient.request('notes/reactions/create', { noteId: resolvedNote.id, reaction }); + await bob.client.request('notes/reactions/create', { noteId: resolvedNote.id, reaction }); await sleep(100); - const reactions = await aliceClient.request('notes/reactions', { noteId: note.id }); + const reactions = await alice.client.request('notes/reactions', { noteId: note.id }); strictEqual(reactions.length, 1); strictEqual(reactions[0].type, reaction); strictEqual(reactions[0].user.id, bobInAServer.id); @@ -184,19 +185,19 @@ describe('Note', () => { describe('Poll', () => { describe('Any remote user\'s vote is delivered to the author', () => { - let carolClient: Misskey.api.APIClient; + let carol: LoginUser; beforeAll(async () => { - [, carolClient] = await createAccount('a.test'); + carol = await createAccount('a.test'); }); test('Bob creates poll and receives a vote from Carol', async () => { - const note = (await bobClient.request('notes/create', { poll: { choices: ['inu', 'neko'] } })).createdNote; - const noteInAServer = await resolveRemoteNote('b.test', note.id, carolClient); - await carolClient.request('notes/polls/vote', { noteId: noteInAServer.id, choice: 0 }); + const note = (await bob.client.request('notes/create', { poll: { choices: ['inu', 'neko'] } })).createdNote; + const noteInAServer = await resolveRemoteNote('b.test', note.id, carol); + await carol.client.request('notes/polls/vote', { noteId: noteInAServer.id, choice: 0 }); await sleep(100); - const noteAfterVote = await bobClient.request('notes/show', { noteId: note.id }); + const noteAfterVote = await bob.client.request('notes/show', { noteId: note.id }); assert(noteAfterVote.poll != null); strictEqual(noteAfterVote.poll.choices[0].votes, 1); strictEqual(noteAfterVote.poll.choices[1].votes, 0); @@ -204,30 +205,29 @@ describe('Note', () => { }); describe('Local user\'s vote is delivered to the author\'s remote followers', () => { - let bobRemoteFollowerClient: Misskey.api.APIClient; - let localVoterClient: Misskey.api.APIClient; + let bobRemoteFollower: LoginUser, localVoter: LoginUser; beforeAll(async () => { [ - [, bobRemoteFollowerClient], - [, localVoterClient], + bobRemoteFollower, + localVoter, ] = await Promise.all([ createAccount('a.test'), createAccount('b.test'), ]); - await bobRemoteFollowerClient.request('following/create', { userId: bobInAServer.id }); + await bobRemoteFollower.client.request('following/create', { userId: bobInAServer.id }); await sleep(100); }); test('A vote in Bob\'s server is delivered to Bob\'s remote followers', async () => { - const note = (await bobClient.request('notes/create', { poll: { choices: ['inu', 'neko'] } })).createdNote; + const note = (await bob.client.request('notes/create', { poll: { choices: ['inu', 'neko'] } })).createdNote; // NOTE: resolve before voting - const noteInAServer = await resolveRemoteNote('b.test', note.id, bobRemoteFollowerClient); - await localVoterClient.request('notes/polls/vote', { noteId: note.id, choice: 0 }); + const noteInAServer = await resolveRemoteNote('b.test', note.id, bobRemoteFollower); + await localVoter.client.request('notes/polls/vote', { noteId: note.id, choice: 0 }); await sleep(100); - const noteAfterVote = await bobRemoteFollowerClient.request('notes/show', { noteId: noteInAServer.id }); + const noteAfterVote = await bobRemoteFollower.client.request('notes/show', { noteId: noteInAServer.id }); assert(noteAfterVote.poll != null); strictEqual(noteAfterVote.poll.choices[0].votes, 1); strictEqual(noteAfterVote.poll.choices[1].votes, 0); diff --git a/packages/backend/test-federation/test/notification.test.ts b/packages/backend/test-federation/test/notification.test.ts index eadb39ebacb8..1cd4674d1f66 100644 --- a/packages/backend/test-federation/test/notification.test.ts +++ b/packages/backend/test-federation/test/notification.test.ts @@ -1,19 +1,20 @@ import { deepStrictEqual, strictEqual } from 'assert'; import * as Misskey from 'misskey-js'; -import { createAccount, isFired, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; +import { createAccount, isFired, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; describe('Notification', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient, aliceUsername: string; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let alice: LoginUser, bob: LoginUser; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient, { username: aliceUsername }] = await createAccount('a.test'); - [bob, bobClient] = await createAccount('b.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, aliceClient), - resolveRemoteUser('a.test', alice.id, bobClient), + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), ]); }); @@ -22,7 +23,7 @@ describe('Notification', () => { const fired = await Promise.all([ isFired( 'b.test', bob, 'main', - async () => await bobClient.request('following/create', { userId: aliceInBServer.id }), + async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), 'follow', msg => msg.id === aliceInBServer.id, ), isFired( @@ -34,31 +35,31 @@ describe('Notification', () => { deepStrictEqual(fired, [true, true]); { - const notifications = await bobClient.request('i/notifications', {}); + const notifications = await bob.client.request('i/notifications', {}); const notification = notifications[0]; strictEqual(notification.type, 'followRequestAccepted'); strictEqual(notification.userId, aliceInBServer.id); } { - const notifications = await aliceClient.request('i/notifications', {}); + const notifications = await alice.client.request('i/notifications', {}); const notification = notifications[0]; strictEqual(notification.type, 'follow'); strictEqual(notification.userId, bobInAServer.id); } }); - afterAll(async () => await bobClient.request('following/delete', { userId: aliceInBServer.id })); + afterAll(async () => await bob.client.request('following/delete', { userId: aliceInBServer.id })); }); describe('Note', () => { test('Get notification when get a reaction', async () => { - const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bobClient); + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); const reaction = '😅'; const fired = await isFired( 'a.test', alice, 'main', - async () => await bobClient.request('notes/reactions/create', { noteId: noteInBServer.id, reaction }), + async () => await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction }), 'notification', msg => { return msg.type === 'reaction' && msg.note.id === note.id && msg.userId === bobInAServer.id && msg.reaction === reaction; }, @@ -67,15 +68,15 @@ describe('Notification', () => { }); test('Get notification when replied', async () => { - const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bobClient); + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); const text = crypto.randomUUID(); const fired = await Promise.all([ isFired( 'a.test', alice, 'main', async () => { await sleep(200); - await bobClient.request('notes/create', { text, replyId: noteInBServer.id }); + await bob.client.request('notes/create', { text, replyId: noteInBServer.id }); }, 'notification', msg => { return msg.type === 'reply' && msg.note.reply!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; @@ -91,14 +92,14 @@ describe('Notification', () => { }); test('Get notification when renoted', async () => { - const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bobClient); + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); const fired = await Promise.all([ isFired( 'a.test', alice, 'main', async () => { await sleep(200); - await bobClient.request('notes/create', { renoteId: noteInBServer.id }); + await bob.client.request('notes/create', { renoteId: noteInBServer.id }); }, 'notification', msg => { return msg.type === 'renote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id; @@ -114,15 +115,15 @@ describe('Notification', () => { }); test('Get notification when quoted', async () => { - const note = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bobClient); + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); const text = crypto.randomUUID(); const fired = await Promise.all([ isFired( 'a.test', alice, 'main', async () => { await sleep(200); - await bobClient.request('notes/create', { text, renoteId: noteInBServer.id }); + await bob.client.request('notes/create', { text, renoteId: noteInBServer.id }); }, 'notification', msg => { return msg.type === 'quote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; @@ -138,13 +139,13 @@ describe('Notification', () => { }); test('Get notification when get a reaction', async () => { - const text = `@${aliceUsername}@a.test`; + const text = `@${alice.username}@a.test`; const fired = await Promise.all([ isFired( 'a.test', alice, 'main', async () => { await sleep(200); - await bobClient.request('notes/create', { text }); + await bob.client.request('notes/create', { text }); }, 'notification', msg => msg.type === 'mention' && msg.userId === bobInAServer.id && msg.note.text === text, ), diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index d2491f232160..cf03281d3e62 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -1,29 +1,30 @@ import { strictEqual } from 'assert'; import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, isFired, type Request, resolveRemoteUser, sleep } from './utils.js'; +import { createAccount, fetchAdmin, isFired, type LoginUser, type Request, resolveRemoteUser, sleep } from './utils.js'; -const [, bAdminClient] = await fetchAdmin('b.test'); +const bAdmin = await fetchAdmin('b.test'); describe('Timeline', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient, bobUsername: string; + let alice: LoginUser, bob: LoginUser; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test'); - [bob, bobClient, { username: bobUsername }] = await createAccount('b.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, aliceClient), - resolveRemoteUser('a.test', alice.id, bobClient), + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), ]); - await bobClient.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInBServer.id }); await sleep(100); }); async function postFromAlice(params?: Misskey.entities.NotesCreateRequest) { - await aliceClient.request('notes/create', { text: 'a', ...params }); + await alice.client.request('notes/create', { text: 'a', ...params }); } type TimelineChannel = keyof Misskey.Channels & (`${string}Timeline` | 'antenna' | 'userList' | 'hashtag'); @@ -61,7 +62,7 @@ describe('Timeline', () => { endpoint === 'notes/search-by-tag' ? { query: (channelParams as Misskey.Channels['hashtag']['params']).q } : endpoint === 'roles/notes' ? { roleId: (channelParams as Misskey.Channels['roleTimeline']['params']).roleId } : {}; - const notes = await (bobClient.request as Request)(endpoint, params); + const notes = await (bob.client.request as Request)(endpoint, params); const endpointFired = notes.some(note => note.text === text); strictEqual(endpointFired, expect); } @@ -100,7 +101,7 @@ describe('Timeline', () => { * @see https://github.com/misskey-dev/misskey/issues/14083 */ test.failing('Don\'t receive remote followee\'s invisible and mentioned specified-only Note', async () => { - await postAndCheckReception(homeTimeline, false, { text: `@${bobUsername}@b.test Hello`, visibility: 'specified' }); + await postAndCheckReception(homeTimeline, false, { text: `@${bob.username}@b.test Hello`, visibility: 'specified' }); }); /** @@ -108,7 +109,7 @@ describe('Timeline', () => { * @see https://github.com/misskey-dev/misskey/issues/14084 */ test.failing('Receive remote followee\'s visible specified-only reply to invisible specified-only Note', async () => { - const note = (await aliceClient.request('notes/create', { text: 'a', visibility: 'specified' })).createdNote; + const note = (await alice.client.request('notes/create', { text: 'a', visibility: 'specified' })).createdNote; await postAndCheckReception(homeTimeline, true, { replyId: note.id, visibility: 'specified', visibleUserIds: [bobInAServer.id] }); }); }); @@ -174,8 +175,8 @@ describe('Timeline', () => { let list: Misskey.entities.UserList; beforeAll(async () => { - list = await bobClient.request('users/lists/create', { name: 'Bob\'s List' }); - await bobClient.request('users/lists/push', { listId: list.id, userId: aliceInBServer.id }); + list = await bob.client.request('users/lists/create', { name: 'Bob\'s List' }); + await bob.client.request('users/lists/push', { listId: list.id, userId: aliceInBServer.id }); await sleep(100); }); @@ -230,7 +231,7 @@ describe('Timeline', () => { let role: Misskey.entities.Role; beforeAll(async () => { - role = await bAdminClient.request('admin/roles/create', { + role = await bAdmin.client.request('admin/roles/create', { name: 'Remote Users', description: 'Remote users are assigned to this role.', color: null, @@ -270,7 +271,7 @@ describe('Timeline', () => { }); afterAll(async () => { - await bAdminClient.request('admin/roles/delete', { roleId: role.id }); + await bAdmin.client.request('admin/roles/delete', { roleId: role.id }); }); }); @@ -281,7 +282,7 @@ describe('Timeline', () => { let bobAntenna: Misskey.entities.Antenna; beforeAll(async () => { - bobAntenna = await bobClient.request('antennas/create', { + bobAntenna = await bob.client.request('antennas/create', { name: 'Bob\'s Egosurfing Antenna', src: 'all', keywords: [['Bob']], @@ -314,7 +315,7 @@ describe('Timeline', () => { }); afterAll(async () => { - await bobClient.request('antennas/delete', { antennaId: bobAntenna.id }); + await bob.client.request('antennas/delete', { antennaId: bobAntenna.id }); }); }); }); diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index bef696dbd0b5..db2f6cfc4005 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -1,21 +1,21 @@ import assert, { rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; +import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; -const [, aAdminClient] = await fetchAdmin('a.test'); +const aAdmin = await fetchAdmin('a.test'); describe('User', () => { describe('Profile', () => { describe('Consistency of profile', () => { - let alice: Misskey.entities.SigninResponse; - let aliceWatcherClient: Misskey.api.APIClient; - let aliceWatcherInBServerClient: Misskey.api.APIClient; + let alice: LoginUser; + let aliceWatcher: LoginUser; + let aliceWatcherInBServer: LoginUser; beforeAll(async () => { - [alice] = await createAccount('a.test'); + alice = await createAccount('a.test'); [ - [, aliceWatcherClient], - [, aliceWatcherInBServerClient], + aliceWatcher, + aliceWatcherInBServer, ] = await Promise.all([ createAccount('a.test'), createAccount('b.test'), @@ -23,9 +23,9 @@ describe('User', () => { }); test('Check consistency', async () => { - const aliceInAServer = await aliceWatcherClient.request('users/show', { userId: alice.id }); - const resolved = await resolveRemoteUser('a.test', aliceInAServer.id, aliceWatcherInBServerClient); - const aliceInBServer = await aliceWatcherInBServerClient.request('users/show', { userId: resolved.id }); + const aliceInAServer = await aliceWatcher.client.request('users/show', { userId: alice.id }); + const resolved = await resolveRemoteUser('a.test', aliceInAServer.id, aliceWatcherInBServer); + const aliceInBServer = await aliceWatcherInBServer.client.request('users/show', { userId: resolved.id }); // console.log(`a.test: ${JSON.stringify(aliceInAServer, null, '\t')}`); // console.log(`b.test: ${JSON.stringify(aliceInBServer, null, '\t')}`); @@ -46,17 +46,18 @@ describe('User', () => { }); describe('isCat is federated', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let alice: LoginUser, bob: LoginUser; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test'); - [bob, bobClient] = await createAccount('b.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, aliceClient), - resolveRemoteUser('a.test', alice.id, bobClient), + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), ]); }); @@ -65,89 +66,91 @@ describe('User', () => { }); test('Becoming a cat is sent to their followers', async () => { - await bobClient.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInBServer.id }); await sleep(100); - await aliceClient.request('i/update', { isCat: true }); + await alice.client.request('i/update', { isCat: true }); await sleep(100); - const res = await bobClient.request('users/show', { userId: aliceInBServer.id }); + const res = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(res.isCat, true); }); }); describe('Pinning Notes', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let alice: LoginUser, bob: LoginUser; let aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test'); - [bob, bobClient] = await createAccount('b.test'); - aliceInBServer = await resolveRemoteUser('a.test', alice.id, bobClient); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + aliceInBServer = await resolveRemoteUser('a.test', alice.id, bob); - await bobClient.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInBServer.id }); }); test('Pinning localOnly Note is not delivered', async () => { - const note = (await aliceClient.request('notes/create', { text: 'a', localOnly: true })).createdNote; - await aliceClient.request('i/pin', { noteId: note.id }); + const note = (await alice.client.request('notes/create', { text: 'a', localOnly: true })).createdNote; + await alice.client.request('i/pin', { noteId: note.id }); await sleep(100); - const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); + const _aliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); }); test('Pinning followers-only Note is not delivered', async () => { - const note = (await aliceClient.request('notes/create', { text: 'a', visibility: 'followers' })).createdNote; - await aliceClient.request('i/pin', { noteId: note.id }); + const note = (await alice.client.request('notes/create', { text: 'a', visibility: 'followers' })).createdNote; + await alice.client.request('i/pin', { noteId: note.id }); await sleep(100); - const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); + const _aliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); }); let pinnedNote: Misskey.entities.Note; test('Pinning normal Note is delivered', async () => { - pinnedNote = (await aliceClient.request('notes/create', { text: 'a' })).createdNote; - await aliceClient.request('i/pin', { noteId: pinnedNote.id }); + pinnedNote = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + await alice.client.request('i/pin', { noteId: pinnedNote.id }); await sleep(100); - const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); + const _aliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 1); - const pinnedNoteInBServer = await resolveRemoteNote('a.test', pinnedNote.id, bobClient); + const pinnedNoteInBServer = await resolveRemoteNote('a.test', pinnedNote.id, bob); strictEqual(_aliceInBServer.pinnedNotes[0].id, pinnedNoteInBServer.id); }); test('Unpinning normal Note is delivered', async () => { - await aliceClient.request('i/unpin', { noteId: pinnedNote.id }); + await alice.client.request('i/unpin', { noteId: pinnedNote.id }); await sleep(100); - const _aliceInBServer = await bobClient.request('users/show', { userId: aliceInBServer.id }); + const _aliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); }); }); }); describe('Follow / Unfollow', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let alice: LoginUser, bob: LoginUser; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test'); - [bob, bobClient] = await createAccount('b.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, aliceClient), - resolveRemoteUser('a.test', alice.id, bobClient), + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), ]); }); describe('Follow a.test ==> b.test', () => { beforeAll(async () => { - await aliceClient.request('following/create', { userId: bobInAServer.id }); + await alice.client.request('following/create', { userId: bobInAServer.id }); await sleep(100); }); @@ -155,12 +158,12 @@ describe('User', () => { test('Check consistency with `users/following` and `users/followers` endpoints', async () => { await Promise.all([ strictEqual( - (await aliceClient.request('users/following', { userId: alice.id })) + (await alice.client.request('users/following', { userId: alice.id })) .some(v => v.followeeId === bobInAServer.id), true, ), strictEqual( - (await bobClient.request('users/followers', { userId: bob.id })) + (await bob.client.request('users/followers', { userId: bob.id })) .some(v => v.followerId === aliceInBServer.id), true, ), @@ -170,7 +173,7 @@ describe('User', () => { describe('Unfollow a.test ==> b.test', () => { beforeAll(async () => { - await aliceClient.request('following/delete', { userId: bobInAServer.id }); + await alice.client.request('following/delete', { userId: bobInAServer.id }); await sleep(100); }); @@ -178,12 +181,12 @@ describe('User', () => { test('Check consistency with `users/following` and `users/followers` endpoints', async () => { await Promise.all([ strictEqual( - (await aliceClient.request('users/following', { userId: alice.id })) + (await alice.client.request('users/following', { userId: alice.id })) .some(v => v.followeeId === bobInAServer.id), false, ), strictEqual( - (await bobClient.request('users/followers', { userId: bob.id })) + (await bob.client.request('users/followers', { userId: bob.id })) .some(v => v.followerId === aliceInBServer.id), false, ), @@ -193,31 +196,32 @@ describe('User', () => { }); describe('Follow requests', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let alice: LoginUser, bob: LoginUser; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test'); - [bob, bobClient] = await createAccount('b.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, aliceClient), - resolveRemoteUser('a.test', alice.id, bobClient), + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), ]); - await aliceClient.request('i/update', { isLocked: true }); + await alice.client.request('i/update', { isLocked: true }); }); describe('Send follow request from Bob to Alice and cancel', () => { describe('Bob sends follow request to Alice', () => { beforeAll(async () => { - await bobClient.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInBServer.id }); await sleep(100); }); test('Alice should have a request', async () => { - const requests = await aliceClient.request('following/requests/list', {}); + const requests = await alice.client.request('following/requests/list', {}); strictEqual(requests.length, 1); strictEqual(requests[0].followee.id, alice.id); strictEqual(requests[0].follower.id, bobInAServer.id); @@ -226,12 +230,12 @@ describe('User', () => { describe('Alice cancels it', () => { beforeAll(async () => { - await bobClient.request('following/requests/cancel', { userId: aliceInBServer.id }); + await bob.client.request('following/requests/cancel', { userId: aliceInBServer.id }); await sleep(100); }); test('Alice should have no requests', async () => { - const requests = await aliceClient.request('following/requests/list', {}); + const requests = await alice.client.request('following/requests/list', {}); strictEqual(requests.length, 0); }); }); @@ -239,16 +243,16 @@ describe('User', () => { describe('Send follow request from Bob to Alice and reject', () => { beforeAll(async () => { - await bobClient.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInBServer.id }); await sleep(100); - await aliceClient.request('following/requests/reject', { userId: bobInAServer.id }); + await alice.client.request('following/requests/reject', { userId: bobInAServer.id }); await sleep(100); }); test('Bob should have no requests', async () => { await rejects( - async () => await bobClient.request('following/requests/cancel', { userId: aliceInBServer.id }), + async () => await bob.client.request('following/requests/cancel', { userId: aliceInBServer.id }), (err: any) => { strictEqual(err.code, 'FOLLOW_REQUEST_NOT_FOUND'); return true; @@ -257,22 +261,22 @@ describe('User', () => { }); test('Bob doesn\'t follow Alice', async () => { - const following = await bobClient.request('users/following', { userId: bob.id }); + const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 0); }); }); describe('Send follow request from Bob to Alice and accept', () => { beforeAll(async () => { - await bobClient.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInBServer.id }); await sleep(100); - await aliceClient.request('following/requests/accept', { userId: bobInAServer.id }); + await alice.client.request('following/requests/accept', { userId: bobInAServer.id }); await sleep(100); }); test('Bob follows Alice', async () => { - const following = await bobClient.request('users/following', { userId: bob.id }); + const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 1); strictEqual(following[0].followeeId, aliceInBServer.id); strictEqual(following[0].followerId, bob.id); @@ -282,35 +286,36 @@ describe('User', () => { describe('Suspension', () => { describe('Check suspend/unsuspend consistency', () => { - let alice: Misskey.entities.SigninResponse, aliceClient: Misskey.api.APIClient; - let bob: Misskey.entities.SigninResponse, bobClient: Misskey.api.APIClient; + let alice: LoginUser, bob: LoginUser; let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { - [alice, aliceClient] = await createAccount('a.test'); - [bob, bobClient] = await createAccount('b.test'); + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, aliceClient), - resolveRemoteUser('a.test', alice.id, bobClient), + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), ]); }); test('Bob follows Alice, and Alice gets suspended, there is no following relation, and Bob fails to follow again', async () => { - await bobClient.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInBServer.id }); await sleep(100); - const followers = await aliceClient.request('users/followers', { userId: alice.id }); + const followers = await alice.client.request('users/followers', { userId: alice.id }); strictEqual(followers.length, 1); // followed by Bob - await aAdminClient.request('admin/suspend-user', { userId: alice.id }); + await aAdmin.client.request('admin/suspend-user', { userId: alice.id }); await sleep(100); - const following = await bobClient.request('users/following', { userId: bob.id }); + const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 0); // no following relation await rejects( - async () => await bobClient.request('following/create', { userId: aliceInBServer.id }), + async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), (err: any) => { strictEqual(err.code, 'NO_SUCH_USER'); return true; @@ -319,10 +324,10 @@ describe('User', () => { }); test('Alice gets unsuspended, Bob succeeds in following Alice', async () => { - await aAdminClient.request('admin/unsuspend-user', { userId: alice.id }); + await aAdmin.client.request('admin/unsuspend-user', { userId: alice.id }); await sleep(100); - const followers = await aliceClient.request('users/followers', { userId: alice.id }); + const followers = await alice.client.request('users/followers', { userId: alice.id }); strictEqual(followers.length, 1); // FIXME: followers are not deleted?? /** @@ -331,7 +336,7 @@ describe('User', () => { * related @see https://github.com/misskey-dev/misskey/issues/13273 */ await rejects( - async () => await bobClient.request('following/create', { userId: aliceInBServer.id }), + async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), (err: any) => { strictEqual(err.code, 'NO_SUCH_USER'); return true; @@ -340,7 +345,7 @@ describe('User', () => { // FIXME: resolving also fails await rejects( - async () => await resolveRemoteUser('a.test', alice.id, bobClient), + async () => await resolveRemoteUser('a.test', alice.id, bob), (err: any) => { strictEqual(err.code, 'INTERNAL_ERROR'); return true; @@ -352,10 +357,10 @@ describe('User', () => { * instead of simple unsuspension, let's tell existence by following from Alice */ test('Alice can follow Bob', async () => { - await aliceClient.request('following/create', { userId: bobInAServer.id }); + await alice.client.request('following/create', { userId: bobInAServer.id }); await sleep(100); - const bobFollowers = await bobClient.request('users/followers', { userId: bob.id }); + const bobFollowers = await bob.client.request('users/followers', { userId: bob.id }); strictEqual(bobFollowers.length, 1); // followed by Alice assert(bobFollowers[0].follower != null); const renewedAliceInBServer = bobFollowers[0].follower; @@ -363,19 +368,19 @@ describe('User', () => { assert(aliceInBServer.host === renewedAliceInBServer.host); assert(aliceInBServer.id !== renewedAliceInBServer.id); // TODO: Same username and host, but their ids are different! Is it OK? - const following = await bobClient.request('users/following', { userId: bob.id }); + const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 0); // following are deleted // Bob tries to follow Alice - await bobClient.request('following/create', { userId: renewedAliceInBServer.id }); + await bob.client.request('following/create', { userId: renewedAliceInBServer.id }); await sleep(100); - const aliceFollowers = await aliceClient.request('users/followers', { userId: alice.id }); + const aliceFollowers = await alice.client.request('users/followers', { userId: alice.id }); strictEqual(aliceFollowers.length, 1); // FIXME: but resolving still fails ... await rejects( - async () => await resolveRemoteUser('a.test', alice.id, bobClient), + async () => await resolveRemoteUser('a.test', alice.id, bob), (err: any) => { strictEqual(err.code, 'INTERNAL_ERROR'); return true; diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 6ef1d829fdef..dc2d4770f37e 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -21,6 +21,12 @@ if (!fetched) { fetched = true; } +export type LoginUser = Misskey.entities.SigninResponse & { + client: Misskey.api.APIClient; + username: string; + password: string; +} + /** used for avoiding overload and some endpoints */ export type Request = ( endpoint: E, params: P, credential?: string | null @@ -85,7 +91,7 @@ async function createAdmin(host: Host): Promise { +export async function fetchAdmin(host: Host): Promise { const admin = adminCache.get(host) ?? await signin(host, ADMIN_PARAMS) .then(res => { adminCache.set(host, res); @@ -101,28 +107,33 @@ export async function fetchAdmin(host: Host): Promise<[Misskey.entities.SigninRe throw err; }); - return [admin, new Misskey.api.APIClient({ origin: `https://${host}`, credential: admin.i })]; + return { + ...admin, + client: new Misskey.api.APIClient({ origin: `https://${host}`, credential: admin.i }), + ...ADMIN_PARAMS, + }; } -export async function createAccount(host: Host): Promise<[Misskey.entities.SigninResponse, Misskey.api.APIClient, { username: string; password: string }]> { +export async function createAccount(host: Host): Promise { const username = crypto.randomUUID().replaceAll('-', '').substring(0, 20); const password = crypto.randomUUID().replaceAll('-', ''); - const [, adminClient] = await fetchAdmin(host); - await adminClient.request('admin/accounts/create', { username, password }); + const admin = await fetchAdmin(host); + await admin.client.request('admin/accounts/create', { username, password }); // console.log(`Created an account: @${username}@${host}`); const signinRes = await signin(host, { username, password }); - return [ - signinRes, - new Misskey.api.APIClient({ origin: `https://${host}`, credential: signinRes.i }), - { username, password }, - ]; + return { + ...signinRes, + client: new Misskey.api.APIClient({ origin: `https://${host}`, credential: signinRes.i }), + username, + password, + }; } -export async function resolveRemoteUser(host: Host, id: string, fromClient: Misskey.api.APIClient): Promise { +export async function resolveRemoteUser(host: Host, id: string, from: LoginUser): Promise { return new Promise((resolve, reject) => { const uri = `https://${host}/users/${id}`; - fromClient.request('ap/show', { uri }) + from.client.request('ap/show', { uri }) .then(res => { strictEqual(res.type, 'User'); strictEqual(res.object.uri, uri); @@ -132,10 +143,10 @@ export async function resolveRemoteUser(host: Host, id: string, fromClient: Miss }); } -export async function resolveRemoteNote(host: Host, id: string, fromClient: Misskey.api.APIClient): Promise { +export async function resolveRemoteNote(host: Host, id: string, from: LoginUser): Promise { return new Promise((resolve, reject) => { const uri = `https://${host}/notes/${id}`; - fromClient.request('ap/show', { uri }) + from.client.request('ap/show', { uri }) .then(res => { strictEqual(res.type, 'Note'); strictEqual(res.object.uri, uri); From a74ce99f8e7f8f1a6ec2ecb8c2cc696b1baf58c3 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:55:23 +0900 Subject: [PATCH 071/111] test(notification): mute --- .../test-federation/test/notification.test.ts | 371 ++++++++++++------ 1 file changed, 244 insertions(+), 127 deletions(-) diff --git a/packages/backend/test-federation/test/notification.test.ts b/packages/backend/test-federation/test/notification.test.ts index 1cd4674d1f66..c455839f4cdf 100644 --- a/packages/backend/test-federation/test/notification.test.ts +++ b/packages/backend/test-federation/test/notification.test.ts @@ -2,160 +2,277 @@ import { deepStrictEqual, strictEqual } from 'assert'; import * as Misskey from 'misskey-js'; import { createAccount, isFired, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; +// TODO: these tests only checks streaming, so API endpoints are not tested describe('Notification', () => { - let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; - - beforeAll(async () => { - [alice, bob] = await Promise.all([ - createAccount('a.test'), - createAccount('b.test'), - ]); - - [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, alice), - resolveRemoteUser('a.test', alice.id, bob), - ]); - }); + describe('Non-mute', () => { + let alice: LoginUser, bob: LoginUser; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; - describe('Follow', () => { - test('Get notification when follow/followed', async () => { - const fired = await Promise.all([ - isFired( - 'b.test', bob, 'main', - async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), - 'follow', msg => msg.id === aliceInBServer.id, - ), - isFired( - 'a.test', alice, 'main', - async () => {}, // NOTE: do nothing because done in above - 'followed', msg => msg.id === bobInAServer.id, - ), + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), ]); - deepStrictEqual(fired, [true, true]); - - { - const notifications = await bob.client.request('i/notifications', {}); - const notification = notifications[0]; - strictEqual(notification.type, 'followRequestAccepted'); - strictEqual(notification.userId, aliceInBServer.id); - } - - { - const notifications = await alice.client.request('i/notifications', {}); - const notification = notifications[0]; - strictEqual(notification.type, 'follow'); - strictEqual(notification.userId, bobInAServer.id); - } }); - afterAll(async () => await bob.client.request('following/delete', { userId: aliceInBServer.id })); - }); + describe('Follow', () => { + test('Get notification when follow/followed', async () => { + const fired = await Promise.all([ + isFired( + 'b.test', bob, 'main', + async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + 'follow', msg => msg.id === aliceInBServer.id, + ), + isFired( + 'a.test', alice, 'main', + async () => {}, // NOTE: do nothing because done in above + 'followed', msg => msg.id === bobInAServer.id, + ), + ]); + deepStrictEqual(fired, [true, true]); + + { + const notifications = await bob.client.request('i/notifications', {}); + const notification = notifications[0]; + strictEqual(notification.type, 'followRequestAccepted'); + strictEqual(notification.userId, aliceInBServer.id); + } - describe('Note', () => { - test('Get notification when get a reaction', async () => { - const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - const reaction = '😅'; - const fired = await isFired( - 'a.test', alice, 'main', - async () => await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction }), - 'notification', msg => { - return msg.type === 'reaction' && msg.note.id === note.id && msg.userId === bobInAServer.id && msg.reaction === reaction; - }, - ); - strictEqual(fired, true); + { + const notifications = await alice.client.request('i/notifications', {}); + const notification = notifications[0]; + strictEqual(notification.type, 'follow'); + strictEqual(notification.userId, bobInAServer.id); + } + }); + + afterAll(async () => await bob.client.request('following/delete', { userId: aliceInBServer.id })); }); - test('Get notification when replied', async () => { - const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - const text = crypto.randomUUID(); - const fired = await Promise.all([ - isFired( + describe('Note', () => { + test('Get notification when get a reaction', async () => { + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const reaction = '😅'; + const fired = await isFired( 'a.test', alice, 'main', - async () => { - await sleep(200); - await bob.client.request('notes/create', { text, replyId: noteInBServer.id }); - }, + async () => await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction }), 'notification', msg => { - return msg.type === 'reply' && msg.note.reply!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; + return msg.type === 'reaction' && msg.note.id === note.id && msg.userId === bobInAServer.id && msg.reaction === reaction; }, - ), - isFired( - 'a.test', alice, 'main', - async () => {}, // NOTE: do nothing because already done above - 'reply', msg => msg.reply!.id === note.id && msg.userId === bobInAServer.id && msg.text === text, - ), + ); + strictEqual(fired, true); + }); + + test('Get notification when replied', async () => { + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const text = crypto.randomUUID(); + const fired = await Promise.all([ + isFired( + 'a.test', alice, 'main', + async () => { + await sleep(200); + await bob.client.request('notes/create', { text, replyId: noteInBServer.id }); + }, + 'notification', msg => { + return msg.type === 'reply' && msg.note.reply!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; + }, + ), + isFired( + 'a.test', alice, 'main', + async () => {}, // NOTE: do nothing because already done above + 'reply', msg => msg.reply!.id === note.id && msg.userId === bobInAServer.id && msg.text === text, + ), + ]); + deepStrictEqual(fired, [true, true]); + }); + + test('Get notification when renoted', async () => { + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const fired = await Promise.all([ + isFired( + 'a.test', alice, 'main', + async () => { + await sleep(200); + await bob.client.request('notes/create', { renoteId: noteInBServer.id }); + }, + 'notification', msg => { + return msg.type === 'renote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id; + }, + ), + isFired( + 'a.test', alice, 'main', + async () => {}, // NOTE: do nothing because already done above + 'renote', msg => msg.renote!.id === note.id && msg.userId === bobInAServer.id, + ), + ]); + deepStrictEqual(fired, [true, true]); + }); + + test('Get notification when quoted', async () => { + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const text = crypto.randomUUID(); + const fired = await Promise.all([ + isFired( + 'a.test', alice, 'main', + async () => { + await sleep(200); + await bob.client.request('notes/create', { text, renoteId: noteInBServer.id }); + }, + 'notification', msg => { + return msg.type === 'quote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; + }, + ), + isFired( + 'a.test', alice, 'main', + async () => {}, // NOTE: do nothing because already done above + 'renote', msg => msg.renote!.id === note.id && msg.userId === bobInAServer.id && msg.text === text, + ), + ]); + deepStrictEqual(fired, [true, true]); + }); + + test('Get notification when get a reaction', async () => { + const text = `@${alice.username}@a.test`; + const fired = await Promise.all([ + isFired( + 'a.test', alice, 'main', + async () => { + await sleep(200); + await bob.client.request('notes/create', { text }); + }, + 'notification', msg => msg.type === 'mention' && msg.userId === bobInAServer.id && msg.note.text === text, + ), + isFired( + 'a.test', alice, 'main', + async () => {}, // NOTE: do nothing because already done above + 'mention', msg => msg.userId === bobInAServer.id && msg.text === text, + ), + ]); + deepStrictEqual(fired, [true, true]); + }); + }); + }); + + describe('Mute', () => { + let alice: LoginUser, bob: LoginUser; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), + ]); + + await Promise.all([ + alice.client.request('mute/create', { userId: bobInAServer.id }), + bob.client.request('mute/create', { userId: aliceInBServer.id }), ]); - deepStrictEqual(fired, [true, true]); + await sleep(100); }); - test('Get notification when renoted', async () => { - const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - const fired = await Promise.all([ - isFired( + describe('Follow', () => { + // NOTE: you cannot mute follow/followed notification + test('Get notification when follow/followed', async () => { + const fired = await Promise.all([ + isFired( + 'b.test', bob, 'main', + async () => { + await sleep(200); + await bob.client.request('following/create', { userId: aliceInBServer.id }); + }, + 'follow', msg => msg.id === aliceInBServer.id, + ), + isFired( + 'a.test', alice, 'main', + async () => {}, // NOTE: do nothing because done in above + 'followed', msg => msg.id === bobInAServer.id, + ), + ]); + deepStrictEqual(fired, [true, true]); + }); + + afterAll(async () => await bob.client.request('following/delete', { userId: aliceInBServer.id })); + }); + + describe('Note', () => { + test('Get no notification when get a reaction', async () => { + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const reaction = '😅'; + const fired = await isFired( 'a.test', alice, 'main', - async () => { - await sleep(200); - await bob.client.request('notes/create', { renoteId: noteInBServer.id }); - }, + async () => await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction }), 'notification', msg => { - return msg.type === 'renote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id; + return msg.type === 'reaction' && msg.note.id === note.id && msg.userId === bobInAServer.id && msg.reaction === reaction; }, - ), - isFired( + ); + strictEqual(fired, false); + }); + + test('Get no notification when replied', async () => { + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const text = crypto.randomUUID(); + const fired = await isFired( 'a.test', alice, 'main', - async () => {}, // NOTE: do nothing because already done above - 'renote', msg => msg.renote!.id === note.id && msg.userId === bobInAServer.id, - ), - ]); - deepStrictEqual(fired, [true, true]); - }); + async () => await bob.client.request('notes/create', { text, replyId: noteInBServer.id }), + 'notification', msg => { + return msg.type === 'reply' && msg.note.reply!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; + }, + ); + deepStrictEqual(fired, false); + }); - test('Get notification when quoted', async () => { - const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - const text = crypto.randomUUID(); - const fired = await Promise.all([ - isFired( + test('Get no notification when renoted', async () => { + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const fired = await isFired( 'a.test', alice, 'main', - async () => { - await sleep(200); - await bob.client.request('notes/create', { text, renoteId: noteInBServer.id }); + async () => await bob.client.request('notes/create', { renoteId: noteInBServer.id }), + 'notification', msg => { + return msg.type === 'renote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id; }, + ); + deepStrictEqual(fired, false); + }); + + test('Get no notification when quoted', async () => { + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const text = crypto.randomUUID(); + const fired = await isFired( + 'a.test', alice, 'main', + async () => await bob.client.request('notes/create', { text, renoteId: noteInBServer.id }), 'notification', msg => { return msg.type === 'quote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; }, - ), - isFired( - 'a.test', alice, 'main', - async () => {}, // NOTE: do nothing because already done above - 'renote', msg => msg.renote!.id === note.id && msg.userId === bobInAServer.id && msg.text === text, - ), - ]); - deepStrictEqual(fired, [true, true]); - }); + ); + deepStrictEqual(fired, false); + }); - test('Get notification when get a reaction', async () => { - const text = `@${alice.username}@a.test`; - const fired = await Promise.all([ - isFired( + test('Get no notification when get a reaction', async () => { + const text = `@${alice.username}@a.test`; + const fired = await isFired( 'a.test', alice, 'main', - async () => { - await sleep(200); - await bob.client.request('notes/create', { text }); - }, + async () => await bob.client.request('notes/create', { text }), 'notification', msg => msg.type === 'mention' && msg.userId === bobInAServer.id && msg.note.text === text, - ), - isFired( - 'a.test', alice, 'main', - async () => {}, // NOTE: do nothing because already done above - 'mention', msg => msg.userId === bobInAServer.id && msg.text === text, - ), - ]); - deepStrictEqual(fired, [true, true]); + ); + deepStrictEqual(fired, false); + }); }); }); }); From eb4af5334f755d679303dbc0c5c0fe22b0cecc87 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 00:02:28 +0900 Subject: [PATCH 072/111] fix(notification): correct description --- packages/backend/test-federation/test/notification.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/test-federation/test/notification.test.ts b/packages/backend/test-federation/test/notification.test.ts index c455839f4cdf..1ccd01c8cb4b 100644 --- a/packages/backend/test-federation/test/notification.test.ts +++ b/packages/backend/test-federation/test/notification.test.ts @@ -140,7 +140,7 @@ describe('Notification', () => { deepStrictEqual(fired, [true, true]); }); - test('Get notification when get a reaction', async () => { + test('Get notification when mentioned', async () => { const text = `@${alice.username}@a.test`; const fired = await Promise.all([ isFired( @@ -264,7 +264,7 @@ describe('Notification', () => { deepStrictEqual(fired, false); }); - test('Get no notification when get a reaction', async () => { + test('Get no notification when mentioned', async () => { const text = `@${alice.username}@a.test`; const fired = await isFired( 'a.test', alice, 'main', From 03a9998a8357126ea6033c5db988edcb48574a32 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 00:04:31 +0900 Subject: [PATCH 073/111] test(block): mention --- .../test-federation/test/block.test.ts | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/packages/backend/test-federation/test/block.test.ts b/packages/backend/test-federation/test/block.test.ts index 81625c8b27d6..9adc08c92bf8 100644 --- a/packages/backend/test-federation/test/block.test.ts +++ b/packages/backend/test-federation/test/block.test.ts @@ -1,6 +1,6 @@ import { deepStrictEqual, rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; +import { createAccount, isFired, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; describe('Block', () => { describe('Check follow', () => { @@ -190,4 +190,38 @@ describe('Block', () => { deepStrictEqual(_note.reactions, { '😅': 1 }); }); }); + + describe('Check mention', () => { + let alice: LoginUser, bob: LoginUser; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), + ]); + }); + + /** NOTE: You should mute the target to stop receiving notifications for now. See {@link file://./notification.test.ts} */ + test('Can mention and notified even if blocked', async () => { + await alice.client.request('blocking/create', { userId: bobInAServer.id }); + await sleep(100); + + const text = `@${alice.username}@a.test plz unblock me!`; + const fired = await isFired( + 'a.test', alice, 'main', + async () => { + await sleep(200); + await bob.client.request('notes/create', { text }); + }, + 'notification', msg => msg.type === 'mention' && msg.userId === bobInAServer.id && msg.note.text === text, + ); + deepStrictEqual(fired, true); + }); + }); }); From 28264be643494ba52c99b58425a8129707a3a389 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 00:16:08 +0900 Subject: [PATCH 074/111] refactor(emoji): addCustomEmoji function --- .../test-federation/test/emoji.test.ts | 56 +++++++------------ .../backend/test-federation/test/utils.ts | 7 +++ 2 files changed, 28 insertions(+), 35 deletions(-) diff --git a/packages/backend/test-federation/test/emoji.test.ts b/packages/backend/test-federation/test/emoji.test.ts index 9d363899dec0..e679c528cffb 100644 --- a/packages/backend/test-federation/test/emoji.test.ts +++ b/packages/backend/test-federation/test/emoji.test.ts @@ -1,8 +1,6 @@ import assert, { deepStrictEqual, strictEqual } from 'assert'; import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, type LoginUser, resolveRemoteUser, sleep, uploadFile } from './utils.js'; - -const aAdmin = await fetchAdmin('a.test'); +import { addCustomEmoji, createAccount, type LoginUser, resolveRemoteUser, sleep } from './utils.js'; describe('Emoji', () => { let alice: LoginUser, bob: LoginUser; @@ -24,72 +22,62 @@ describe('Emoji', () => { }); test('Custom emoji are delivered with Note delivery', async () => { - const name = crypto.randomUUID().replaceAll('-', ''); - const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); - await aAdmin.client.request('admin/emoji/add', { name, fileId: file.id }); - await alice.client.request('notes/create', { text: `I love :${name}:` }); + const emoji = await addCustomEmoji('a.test'); + await alice.client.request('notes/create', { text: `I love :${emoji.name}:` }); await sleep(100); const notes = await bob.client.request('notes/timeline', {}); const noteInBServer = notes[0]; - strictEqual(noteInBServer.text, `I love \u200b:${name}:\u200b`); + strictEqual(noteInBServer.text, `I love \u200b:${emoji.name}:\u200b`); assert(noteInBServer.emojis != null); - assert(name in noteInBServer.emojis); - strictEqual(noteInBServer.emojis[name], file.url); + assert(emoji.name in noteInBServer.emojis); + strictEqual(noteInBServer.emojis[emoji.name], emoji.url); }); test('Custom emoji are delivered with Reaction delivery', async () => { - const name = crypto.randomUUID().replaceAll('-', ''); - const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); - await aAdmin.client.request('admin/emoji/add', { name, fileId: file.id }); + const emoji = await addCustomEmoji('a.test'); const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; await sleep(100); - await alice.client.request('notes/reactions/create', { noteId: note.id, reaction: `:${name}:` }); + await alice.client.request('notes/reactions/create', { noteId: note.id, reaction: `:${emoji.name}:` }); await sleep(100); const noteInBServer = (await bob.client.request('notes/timeline', {}))[0]; - deepStrictEqual(noteInBServer.reactions[`:${name}@a.test:`], 1); - deepStrictEqual(noteInBServer.reactionEmojis[`${name}@a.test`], file.url); + deepStrictEqual(noteInBServer.reactions[`:${emoji.name}@a.test:`], 1); + deepStrictEqual(noteInBServer.reactionEmojis[`${emoji.name}@a.test`], emoji.url); }); test('Custom emoji are delivered with Profile delivery', async () => { - const name = crypto.randomUUID().replaceAll('-', ''); - const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); - await aAdmin.client.request('admin/emoji/add', { name, fileId: file.id }); - const renewedAlice = await alice.client.request('i/update', { name: `:${name}:` }); + const emoji = await addCustomEmoji('a.test'); + const renewedAlice = await alice.client.request('i/update', { name: `:${emoji.name}:` }); await sleep(100); const renewedAliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(renewedAliceInBServer.name, renewedAlice.name); - assert(name in renewedAliceInBServer.emojis); - strictEqual(renewedAliceInBServer.emojis[name], file.url); + assert(emoji.name in renewedAliceInBServer.emojis); + strictEqual(renewedAliceInBServer.emojis[emoji.name], emoji.url); }); test('Local-only custom emoji aren\'t delivered with Note delivery', async () => { - const name = crypto.randomUUID().replaceAll('-', ''); - const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); - await aAdmin.client.request('admin/emoji/add', { name, fileId: file.id, localOnly: true }); - await alice.client.request('notes/create', { text: `I love :${name}:` }); + const emoji = await addCustomEmoji('a.test', { localOnly: true }); + await alice.client.request('notes/create', { text: `I love :${emoji.name}:` }); await sleep(100); const notes = await bob.client.request('notes/timeline', {}); const noteInBServer = notes[0]; - strictEqual(noteInBServer.text, `I love \u200b:${name}:\u200b`); + strictEqual(noteInBServer.text, `I love \u200b:${emoji.name}:\u200b`); // deepStrictEqual(noteInBServer.emojis, {}); // TODO: this fails (why?) deepStrictEqual({ ...noteInBServer.emojis }, {}); }); test('Local-only custom emoji aren\'t delivered with Reaction delivery', async () => { - const name = crypto.randomUUID().replaceAll('-', ''); - const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); - await aAdmin.client.request('admin/emoji/add', { name, fileId: file.id, localOnly: true }); + const emoji = await addCustomEmoji('a.test', { localOnly: true }); const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; await sleep(100); - await alice.client.request('notes/reactions/create', { noteId: note.id, reaction: `:${name}:` }); + await alice.client.request('notes/reactions/create', { noteId: note.id, reaction: `:${emoji.name}:` }); await sleep(100); const noteInBServer = (await bob.client.request('notes/timeline', {}))[0]; @@ -98,10 +86,8 @@ describe('Emoji', () => { }); test('Local-only custom emoji aren\'t delivered with Profile delivery', async () => { - const name = crypto.randomUUID().replaceAll('-', ''); - const file = await uploadFile('a.test', '../../test/resources/192.jpg', aAdmin.i); - await aAdmin.client.request('admin/emoji/add', { name, fileId: file.id, localOnly: true }); - const renewedAlice = await alice.client.request('i/update', { name: `:${name}:` }); + const emoji = await addCustomEmoji('a.test', { localOnly: true }); + const renewedAlice = await alice.client.request('i/update', { name: `:${emoji.name}:` }); await sleep(100); const renewedAliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index dc2d4770f37e..5bf8e6bf6f9e 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -178,6 +178,13 @@ export async function uploadFile(host: Host, path: string, token: string): Promi }); } +export async function addCustomEmoji(host: Host, param?: Partial, path = '../../test/resources/192.jpg'): Promise { + const admin = await fetchAdmin(host); + const name = crypto.randomUUID().replaceAll('-', ''); + const file = await uploadFile('a.test', path, admin.i); + return await admin.client.request('admin/emoji/add', { name, fileId: file.id, ...param }); +} + export function deepStrictEqualWithExcludedFields(actual: T, expected: T, excludedFields: (keyof T)[]) { const _actual = structuredClone(actual); const _expected = structuredClone(expected); From c5936a283bc94564af85a3636fb26c6828c28176 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 00:21:06 +0900 Subject: [PATCH 075/111] fix: typo --- packages/backend/test-federation/test/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 5bf8e6bf6f9e..a3a567ac1b3d 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -181,7 +181,7 @@ export async function uploadFile(host: Host, path: string, token: string): Promi export async function addCustomEmoji(host: Host, param?: Partial, path = '../../test/resources/192.jpg'): Promise { const admin = await fetchAdmin(host); const name = crypto.randomUUID().replaceAll('-', ''); - const file = await uploadFile('a.test', path, admin.i); + const file = await uploadFile(host, path, admin.i); return await admin.client.request('admin/emoji/add', { name, fileId: file.id, ...param }); } From 4b30001e33592a68231250cfe0d23ed51e1f0162 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 00:29:50 +0900 Subject: [PATCH 076/111] test(note): add reaction tests --- .../backend/test-federation/test/note.test.ts | 67 ++++++++++++++++--- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index 1bc85f212ff5..73319ccaf28b 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -1,6 +1,6 @@ import assert, { rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, deepStrictEqualWithExcludedFields, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep, uploadFile } from './utils.js'; +import { addCustomEmoji, createAccount, deepStrictEqualWithExcludedFields, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep, uploadFile } from './utils.js'; describe('Note', () => { let alice: LoginUser, bob: LoginUser; @@ -169,17 +169,62 @@ describe('Note', () => { }); describe('Reaction', () => { - test('Consistency of reaction', async () => { - const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); - const reaction = '😅'; - await bob.client.request('notes/reactions/create', { noteId: resolvedNote.id, reaction }); - await sleep(100); + describe('Consistency', () => { + test('Unicode reaction', async () => { + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); + const reaction = '😅'; + await bob.client.request('notes/reactions/create', { noteId: resolvedNote.id, reaction }); + await sleep(100); + + const reactions = await alice.client.request('notes/reactions', { noteId: note.id }); + strictEqual(reactions.length, 1); + strictEqual(reactions[0].type, reaction); + strictEqual(reactions[0].user.id, bobInAServer.id); + }); - const reactions = await alice.client.request('notes/reactions', { noteId: note.id }); - strictEqual(reactions.length, 1); - strictEqual(reactions[0].type, reaction); - strictEqual(reactions[0].user.id, bobInAServer.id); + test('Custom emoji reaction', async () => { + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); + const emoji = await addCustomEmoji('b.test'); + await bob.client.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: `:${emoji.name}:` }); + await sleep(100); + + const reactions = await alice.client.request('notes/reactions', { noteId: note.id }); + strictEqual(reactions.length, 1); + strictEqual(reactions[0].type, `:${emoji.name}@b.test:`); + strictEqual(reactions[0].user.id, bobInAServer.id); + }); + }); + + describe('Acceptance', () => { + test('Even if likeOnly, remote users can react with custom emoji, but it is converted to like', async () => { + const note = (await alice.client.request('notes/create', { text: 'a', reactionAcceptance: 'likeOnly' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const emoji = await addCustomEmoji('b.test'); + await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction: `:${emoji.name}:` }); + await sleep(100); + + const reactions = await alice.client.request('notes/reactions', { noteId: note.id }); + strictEqual(reactions.length, 1); + strictEqual(reactions[0].type, '❤'); + }); + + /** + * TODO: this may be unexpected behavior? + * @see https://github.com/misskey-dev/misskey/issues/12409 + */ + test('Even if nonSensitiveOnly, remote users can react with sensitive emoji, and it is not converted', async () => { + const note = (await alice.client.request('notes/create', { text: 'a', reactionAcceptance: 'nonSensitiveOnly' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const emoji = await addCustomEmoji('b.test', { isSensitive: true }); + await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction: `:${emoji.name}:` }); + await sleep(100); + + const reactions = await alice.client.request('notes/reactions', { noteId: note.id }); + strictEqual(reactions.length, 1); + strictEqual(reactions[0].type, `:${emoji.name}@b.test:`); + }); }); }); From 5c99def196786abaa48ab31dc2a56bf05d69e96d Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:06:56 +0900 Subject: [PATCH 077/111] test(timeline): Note deletion --- .../test-federation/test/timeline.test.ts | 32 +++++++++++++---- .../backend/test-federation/test/utils.ts | 34 +++++++++++++++++++ 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index cf03281d3e62..9a53cd0c0763 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -1,6 +1,6 @@ import { strictEqual } from 'assert'; import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, isFired, type LoginUser, type Request, resolveRemoteUser, sleep } from './utils.js'; +import { createAccount, fetchAdmin, isNoteUpdatedEventFired, isFired, type LoginUser, type Request, resolveRemoteUser, sleep } from './utils.js'; const bAdmin = await fetchAdmin('b.test'); @@ -23,10 +23,6 @@ describe('Timeline', () => { await sleep(100); }); - async function postFromAlice(params?: Misskey.entities.NotesCreateRequest) { - await alice.client.request('notes/create', { text: 'a', ...params }); - } - type TimelineChannel = keyof Misskey.Channels & (`${string}Timeline` | 'antenna' | 'userList' | 'hashtag'); type TimelineEndpoint = keyof Misskey.Endpoints & (`${string}timeline` | 'antennas/notes' | 'roles/notes' | 'notes/search-by-tag'); const timelineMap = new Map([ @@ -46,10 +42,13 @@ describe('Timeline', () => { noteParams: Misskey.entities.NotesCreateRequest = {}, channelParams: Misskey.Channels[C]['params'] = {}, ) { + let note: Misskey.entities.Note | undefined; const text = noteParams.text ?? crypto.randomUUID(); const streamingFired = await isFired( 'b.test', bob, timelineChannel, - async () => await postFromAlice({ text, ...noteParams }), + async () => { + note = (await alice.client.request('notes/create', { text, ...noteParams })).createdNote; + }, 'note', msg => msg.text === text, channelParams, ); @@ -62,9 +61,28 @@ describe('Timeline', () => { endpoint === 'notes/search-by-tag' ? { query: (channelParams as Misskey.Channels['hashtag']['params']).q } : endpoint === 'roles/notes' ? { roleId: (channelParams as Misskey.Channels['roleTimeline']['params']).roleId } : {}; + + await sleep(100); const notes = await (bob.client.request as Request)(endpoint, params); - const endpointFired = notes.some(note => note.text === text); + const noteInBServer = notes.filter(({ uri }) => uri === `https://a.test/notes/${note!.id}`).pop(); + const endpointFired = noteInBServer != null; strictEqual(endpointFired, expect); + + // Let's check Delete reception + if (expect) { + const streamingFired = await isNoteUpdatedEventFired( + 'b.test', bob, noteInBServer!.id, + async () => await alice.client.request('notes/delete', { noteId: note!.id }), + /** @ts-expect-error @see https://github.com/misskey-dev/misskey/pull/14632 */ + msg => msg.type === 'deleted' && msg.id === noteInBServer!.id, + ); + strictEqual(streamingFired, true); + + await sleep(100); + const notes = await (bob.client.request as Request)(endpoint, params); + const endpointFired = notes.every(({ uri }) => uri !== `https://a.test/notes/${note!.id}`); + strictEqual(endpointFired, true); + } } describe('homeTimeline', () => { diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index a3a567ac1b3d..e3cbb84f3d98 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -230,3 +230,37 @@ export async function isFired Promise, + cond: (msg: Parameters[0]) => boolean, +): Promise { + return new Promise(async (resolve, reject) => { + // @ts-expect-error TODO: why? + const stream = new Misskey.Stream(`wss://${host}`, { token: user.i }, { WebSocket }); + stream.send('s', { id: noteId }); + stream.on('noteUpdated', ((msg: any) => { + if (cond(msg)) { + stream.close(); + clearTimeout(timer); + resolve(true); + } + }) as any); + + let timer: NodeJS.Timeout | undefined; + + await trigger().then(() => { + timer = setTimeout(() => { + stream.close(); + resolve(false); + }, 1000); + }).catch(err => { + stream.close(); + clearTimeout(timer); + reject(err); + }); + }); +}; From 76d64043386d64c3971e64b37cdeacceaa162cfc Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:08:05 +0900 Subject: [PATCH 078/111] fix: unnecessary ts-expect-error --- packages/backend/test-federation/test/timeline.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index 9a53cd0c0763..ebd04acba547 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -73,7 +73,6 @@ describe('Timeline', () => { const streamingFired = await isNoteUpdatedEventFired( 'b.test', bob, noteInBServer!.id, async () => await alice.client.request('notes/delete', { noteId: note!.id }), - /** @ts-expect-error @see https://github.com/misskey-dev/misskey/pull/14632 */ msg => msg.type === 'deleted' && msg.id === noteInBServer!.id, ); strictEqual(streamingFired, true); From b8d5c834a761fc36ed97eb41430bfdb023203a6d Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:25:00 +0900 Subject: [PATCH 079/111] refactor: unnecessary fetch mocking --- packages/backend/test-federation/test/utils.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index e3cbb84f3d98..f218cbdbebee 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -42,16 +42,7 @@ async function signin(host: Host, params: Misskey.entities.SigninRequest): Promi // wait for a second to prevent hit rate limit await sleep(1000); // console.log(`Sign in to @${params.username}@${host} ...`); - return await (new Misskey.api.APIClient({ - origin: `https://${host}`, - fetch: (input, init) => fetch(input, { - ...init, - headers: { - ...init?.headers, - 'Content-Type': init?.headers['Content-Type'] != null ? init.headers['Content-Type'] : 'application/json', - }, - }), - }).request as Request)('signin', params) + return await (new Misskey.api.APIClient({ origin: `https://${host}` }).request as Request)('signin', params) .then(res => { // console.log(`Signed in to @${params.username}@${host}`); return res; From 3734cf954fd96d1eed956e5a1365f9c6aec13ba0 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:31:14 +0900 Subject: [PATCH 080/111] chore: add TODO comments --- packages/backend/test-federation/test/timeline.test.ts | 1 + packages/backend/test-federation/test/utils.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index ebd04acba547..423b62cafa25 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -255,6 +255,7 @@ describe('Timeline', () => { iconUrl: null, target: 'conditional', condFormula: { + /** TODO: @see https://github.com/misskey-dev/misskey/issues/14169 */ type: 'isRemote' as never, }, isPublic: true, diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index f218cbdbebee..31ffac406c2c 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -69,6 +69,7 @@ async function createAdmin(host: Host): Promise { await client.request('admin/roles/update-default-policies', { policies: { + /** TODO: @see https://github.com/misskey-dev/misskey/issues/14169 */ rateLimitFactor: 0 as never, }, }, res.token); From 28cbc78f9b61002f37f1e99a6283d14b8a9d2195 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 19:13:34 +0900 Subject: [PATCH 081/111] test(user): deletion --- .../backend/test-federation/test/user.test.ts | 96 ++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index db2f6cfc4005..08948c1b5b73 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -2,7 +2,10 @@ import assert, { rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; -const aAdmin = await fetchAdmin('a.test'); +const [aAdmin, bAdmin] = await Promise.all([ + fetchAdmin('a.test'), + fetchAdmin('b.test'), +]); describe('User', () => { describe('Profile', () => { @@ -284,6 +287,97 @@ describe('User', () => { }); }); + describe('Deletion', () => { + describe('Check Delete consistency', () => { + let alice: LoginUser, bob: LoginUser; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), + ]); + }); + + test('Bob follows Alice, and Alice deleted themself', async () => { + await bob.client.request('following/create', { userId: aliceInBServer.id }); + await sleep(100); + + const followers = await alice.client.request('users/followers', { userId: alice.id }); + strictEqual(followers.length, 1); // followed by Bob + + await alice.client.request('i/delete-account', { password: alice.password }); + await sleep(100); + + const following = await bob.client.request('users/following', { userId: bob.id }); + strictEqual(following.length, 0); // no following relation + + await rejects( + async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + (err: any) => { + strictEqual(err.code, 'NO_SUCH_USER'); + return true; + }, + ); + }); + }); + + describe('Deletion of remote user for moderation', () => { + let alice: LoginUser, bob: LoginUser; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), + ]); + }); + + test('Bob follows Alice, then Alice gets deleted in B server', async () => { + await bob.client.request('following/create', { userId: aliceInBServer.id }); + await sleep(100); + + const followers = await alice.client.request('users/followers', { userId: alice.id }); + strictEqual(followers.length, 1); // followed by Bob + + await bAdmin.client.request('admin/delete-account', { userId: aliceInBServer.id }); + await sleep(100); + + // TODO: why still following relation? + const following = await bob.client.request('users/following', { userId: bob.id }); + strictEqual(following.length, 1); + await rejects( + async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + (err: any) => { + strictEqual(err.code, 'ALREADY_FOLLOWING'); + return true; + }, + ); + }); + + test('Alice tries to follow Bob, but it is not processed', async () => { + await alice.client.request('following/create', { userId: bobInAServer.id }); + await sleep(100); + + const following = await alice.client.request('users/following', { userId: alice.id }); + strictEqual(following.length, 0); // Not following Bob because B server doesn't return Accept + + const followers = await bob.client.request('users/followers', { userId: bob.id }); + strictEqual(followers.length, 0); // Alice's Follow is not processed + }); + }); + }); + describe('Suspension', () => { describe('Check suspend/unsuspend consistency', () => { let alice: LoginUser, bob: LoginUser; From 02627baaf07c04a1c2d4fee3a3c807a92e3958aa Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 20:50:33 +0900 Subject: [PATCH 082/111] chore: enable --frozen-lockfile --- packages/backend/test-federation/compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend/test-federation/compose.yml b/packages/backend/test-federation/compose.yml index 2019d6b20307..51ad01cbd94f 100644 --- a/packages/backend/test-federation/compose.yml +++ b/packages/backend/test-federation/compose.yml @@ -62,8 +62,8 @@ services: entrypoint: > bash -c ' corepack enable && corepack prepare - pnpm -F misskey-js i - pnpm -F backend i + pnpm -F misskey-js i --frozen-lockfile + pnpm -F backend i --frozen-lockfile exec "$0" "$@" ' command: pnpm -F backend test:fed @@ -104,7 +104,7 @@ services: command: > bash -c " corepack enable && corepack prepare - pnpm -F backend i + pnpm -F backend i --frozen-lockfile pnpm exec tsc -p ./packages/backend/test-federation node ./packages/backend/test-federation/built/daemon.js " From 1e03b84b5cd7490224d4033771e5a046f04cfe56 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 22:27:13 +0900 Subject: [PATCH 083/111] fix(ci): copying configs --- .github/workflows/test-federation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml index 9320f41e1c28..1ca38647f89c 100644 --- a/.github/workflows/test-federation.yml +++ b/.github/workflows/test-federation.yml @@ -42,7 +42,7 @@ jobs: - name: Setup run: | cd packages/backend/test-federation - cp ./.env.example ./.env + cp ./.config/example.docker.env ./.config/docker.env bash ./generate_certificates.sh sudo chmod 644 ./certificates/*.test.key - name: Start servers From 16e51c4f27551280b62f36f1c123f36f068e462c Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 23:09:52 +0900 Subject: [PATCH 084/111] docs: update CONTRIBUTING.md --- CONTRIBUTING.md | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f61311f1e594..4db924a79a7f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -181,31 +181,45 @@ MK_DEV_PREFER=backend pnpm dev - HMR may not work in some environments such as Windows. ## Testing -- Test codes are located in [`/packages/backend/test`](/packages/backend/test). - -### Run test -Create a config file. +You can run non-backend tests by executing following commands +```sh +pnpm --filter frontend test +pnpm --filter misskey-js test ``` + +Backend tests require manual preparation of servers. See the next section for more on this. + +### Backend +There are three types of test codes for the backend: +- Unit tests: [`/packages/backend/test/unit`](/packages/backend/test/unit) +- Single-server E2E tests: [`/packages/backend/test/e2e`](/packages/backend/test/e2e) +- Multiple-server E2E tests: [`/packages/backend/test-federation`](/packages/backend/test-federation) + +#### Running Unit Tests or Single-server E2E Tests +1. Create a config file: +```sh cp .github/misskey/test.yml .config/ ``` -Prepare DB/Redis for testing. -``` -docker compose -f packages/backend/test/compose.yml up -``` -Alternatively, prepare an empty (data can be erased) DB and edit `.config/test.yml`. -Run all test. -``` -pnpm test +2. Start DB and Redis servers for testing: +```sh +docker compose -f packages/backend/test/compose.yml up ``` +Instead, you can prepare an empty (data can be erased) DB and edit `.config/test.yml` appropriately. -#### Run specify test +3. Run all tests: +```sh +pnpm --filter backend test # unit tests +pnpm --filter backend test:e2e # single-server E2E tests ``` -pnpm jest -- foo.ts +If you want to run a specific test, run as a following command: +```sh +pnpm --filter backend test -- packages/backend/test/unit/activitypub.ts +pnpm --filter backend test:e2e -- packages/backend/test/e2e/nodeinfo.ts ``` -### e2e tests -TODO +#### Running Multiple-server E2E Tests +See [`/packages/backend/test-federation/README.md`](/packages/backend/test-federation/README.md). ## Environment Variable From e942d5fe8aff7d57c92a9523d083d4d01122371f Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 23:10:04 +0900 Subject: [PATCH 085/111] docs: fix typo --- packages/backend/test-federation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test-federation/README.md b/packages/backend/test-federation/README.md index 57a34a6f4106..733b8b0c2d25 100644 --- a/packages/backend/test-federation/README.md +++ b/packages/backend/test-federation/README.md @@ -1,7 +1,7 @@ ## test-federation Test federation between two Misskey servers: `a.test` and `b.test`. -First, you need to start server by executing following commands: +First, you need to start servers by executing following commands: ```sh cp ./.config/example.docker.env ./.config/docker.env bash ./generate_certificates.sh From ba69e07768d2b10a0eb633a14cfcf28c4b4f4193 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 23:23:01 +0900 Subject: [PATCH 086/111] chore: set default sleep duration --- .../test-federation/test/block.test.ts | 24 +++++----- .../test-federation/test/drive.test.ts | 6 +-- .../test-federation/test/emoji.test.ts | 18 +++---- .../backend/test-federation/test/move.test.ts | 2 +- .../backend/test-federation/test/note.test.ts | 20 ++++---- .../test-federation/test/notification.test.ts | 12 ++--- .../test-federation/test/timeline.test.ts | 12 ++--- .../backend/test-federation/test/user.test.ts | 48 +++++++++---------- .../backend/test-federation/test/utils.ts | 2 +- 9 files changed, 72 insertions(+), 72 deletions(-) diff --git a/packages/backend/test-federation/test/block.test.ts b/packages/backend/test-federation/test/block.test.ts index 9adc08c92bf8..1e383d470df9 100644 --- a/packages/backend/test-federation/test/block.test.ts +++ b/packages/backend/test-federation/test/block.test.ts @@ -21,7 +21,7 @@ describe('Block', () => { test('Cannot follow if blocked', async () => { await alice.client.request('blocking/create', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); await rejects( async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), (err: any) => { @@ -40,7 +40,7 @@ describe('Block', () => { test('Cannot follow even if unblocked', async () => { // unblock here await alice.client.request('blocking/delete', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); // TODO: why still being blocked? await rejects( @@ -54,10 +54,10 @@ describe('Block', () => { test.skip('Can follow if unblocked', async () => { await alice.client.request('blocking/delete', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); await bob.client.request('following/create', { userId: aliceInBServer.id }); - await sleep(100); + await sleep(); const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 1); @@ -74,7 +74,7 @@ describe('Block', () => { }); await alice.client.request('blocking/create', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); test('after block', async () => { const following = await bob.client.request('users/following', { userId: bob.id }); @@ -103,7 +103,7 @@ describe('Block', () => { test('Cannot reply if blocked', async () => { await alice.client.request('blocking/create', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); @@ -118,7 +118,7 @@ describe('Block', () => { test('Can reply if unblocked', async () => { await alice.client.request('blocking/delete', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); @@ -146,7 +146,7 @@ describe('Block', () => { test('Cannot reaction if blocked', async () => { await alice.client.request('blocking/create', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); @@ -163,7 +163,7 @@ describe('Block', () => { test('Cannot reaction even if unblocked', async () => { // unblock here await alice.client.request('blocking/delete', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); @@ -180,7 +180,7 @@ describe('Block', () => { test.skip('Can reaction if unblocked', async () => { await alice.client.request('blocking/delete', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); @@ -210,13 +210,13 @@ describe('Block', () => { /** NOTE: You should mute the target to stop receiving notifications for now. See {@link file://./notification.test.ts} */ test('Can mention and notified even if blocked', async () => { await alice.client.request('blocking/create', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); const text = `@${alice.username}@a.test plz unblock me!`; const fired = await isFired( 'a.test', alice, 'main', async () => { - await sleep(200); + await sleep(); await bob.client.request('notes/create', { text }); }, 'notification', msg => msg.type === 'mention' && msg.userId === bobInAServer.id && msg.note.text === text, diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index a0f3f10f84ba..39325ed88d45 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -105,14 +105,14 @@ describe('Drive', () => { ]); await bob.client.request('following/create', { userId: aliceInBServer.id }); - await sleep(100); + await sleep(); }); test('Alice uploads sensitive image and it is shown as sensitive from Bob', async () => { const file = await uploadFile('a.test', '../../test/resources/192.jpg', alice.i); await alice.client.request('drive/files/update', { fileId: file.id, isSensitive: true }); await alice.client.request('notes/create', { text: 'sensitive', fileIds: [file.id] }); - await sleep(100); + await sleep(); const notes = await bob.client.request('notes/timeline', {}); strictEqual(notes.length, 1); @@ -163,7 +163,7 @@ describe('Drive', () => { await alice.client.request('drive/files/update', { fileId: file.id, isSensitive: true }); const bobNoteInAServer = await resolveRemoteNote('b.test', bobNote.id, alice); const note = (await alice.client.request('notes/create', { text: 'sensitive', fileIds: [file.id], replyId: bobNoteInAServer.id })).createdNote; - await sleep(1000); + await sleep(); const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); assert(noteInBServer.files != null); diff --git a/packages/backend/test-federation/test/emoji.test.ts b/packages/backend/test-federation/test/emoji.test.ts index e679c528cffb..35ee226168b3 100644 --- a/packages/backend/test-federation/test/emoji.test.ts +++ b/packages/backend/test-federation/test/emoji.test.ts @@ -18,13 +18,13 @@ describe('Emoji', () => { ]); await bob.client.request('following/create', { userId: aliceInBServer.id }); - await sleep(100); + await sleep(); }); test('Custom emoji are delivered with Note delivery', async () => { const emoji = await addCustomEmoji('a.test'); await alice.client.request('notes/create', { text: `I love :${emoji.name}:` }); - await sleep(100); + await sleep(); const notes = await bob.client.request('notes/timeline', {}); const noteInBServer = notes[0]; @@ -38,10 +38,10 @@ describe('Emoji', () => { test('Custom emoji are delivered with Reaction delivery', async () => { const emoji = await addCustomEmoji('a.test'); const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - await sleep(100); + await sleep(); await alice.client.request('notes/reactions/create', { noteId: note.id, reaction: `:${emoji.name}:` }); - await sleep(100); + await sleep(); const noteInBServer = (await bob.client.request('notes/timeline', {}))[0]; deepStrictEqual(noteInBServer.reactions[`:${emoji.name}@a.test:`], 1); @@ -51,7 +51,7 @@ describe('Emoji', () => { test('Custom emoji are delivered with Profile delivery', async () => { const emoji = await addCustomEmoji('a.test'); const renewedAlice = await alice.client.request('i/update', { name: `:${emoji.name}:` }); - await sleep(100); + await sleep(); const renewedAliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(renewedAliceInBServer.name, renewedAlice.name); @@ -62,7 +62,7 @@ describe('Emoji', () => { test('Local-only custom emoji aren\'t delivered with Note delivery', async () => { const emoji = await addCustomEmoji('a.test', { localOnly: true }); await alice.client.request('notes/create', { text: `I love :${emoji.name}:` }); - await sleep(100); + await sleep(); const notes = await bob.client.request('notes/timeline', {}); const noteInBServer = notes[0]; @@ -75,10 +75,10 @@ describe('Emoji', () => { test('Local-only custom emoji aren\'t delivered with Reaction delivery', async () => { const emoji = await addCustomEmoji('a.test', { localOnly: true }); const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - await sleep(100); + await sleep(); await alice.client.request('notes/reactions/create', { noteId: note.id, reaction: `:${emoji.name}:` }); - await sleep(100); + await sleep(); const noteInBServer = (await bob.client.request('notes/timeline', {}))[0]; deepStrictEqual({ ...noteInBServer.reactions }, { '❤': 1 }); @@ -88,7 +88,7 @@ describe('Emoji', () => { test('Local-only custom emoji aren\'t delivered with Profile delivery', async () => { const emoji = await addCustomEmoji('a.test', { localOnly: true }); const renewedAlice = await alice.client.request('i/update', { name: `:${emoji.name}:` }); - await sleep(100); + await sleep(); const renewedAliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(renewedAliceInBServer.name, renewedAlice.name); diff --git a/packages/backend/test-federation/test/move.test.ts b/packages/backend/test-federation/test/move.test.ts index 8544e305e548..88fda37d2e8d 100644 --- a/packages/backend/test-federation/test/move.test.ts +++ b/packages/backend/test-federation/test/move.test.ts @@ -30,7 +30,7 @@ describe('Move', () => { // Move @alice@a.test ==> @bob@b.test await bob.client.request('i/update', { alsoKnownAs: [`@${alice.username}@a.test`] }); await alice.client.request('i/move', { moveToAccount: `@${bob.username}@b.test` }); - await sleep(100); + await sleep(); }); test('Check from follower', async () => { diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index 73319ccaf28b..88103c34634a 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -85,7 +85,7 @@ describe('Note', () => { ]); strictEqual(aliceInBServer.id, resolvedNote.userId); - await sleep(100); + await sleep(); const resolvedReplyedNote = await bob.client.request('notes/show', { noteId: resolvedNote.replyId }); strictEqual(resolvedReplyedNote.repliesCount, 1); @@ -149,14 +149,14 @@ describe('Note', () => { carol = await createAccount('a.test'); await carol.client.request('following/create', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); }); test('Delete is derivered to followers', async () => { const note = (await bob.client.request('notes/create', { text: 'I\'m Bob.' })).createdNote; const noteInAServer = await resolveRemoteNote('b.test', note.id, carol); await bob.client.request('notes/delete', { noteId: note.id }); - await sleep(100); + await sleep(); await rejects( async () => await carol.client.request('notes/show', { noteId: noteInAServer.id }), @@ -175,7 +175,7 @@ describe('Note', () => { const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); const reaction = '😅'; await bob.client.request('notes/reactions/create', { noteId: resolvedNote.id, reaction }); - await sleep(100); + await sleep(); const reactions = await alice.client.request('notes/reactions', { noteId: note.id }); strictEqual(reactions.length, 1); @@ -188,7 +188,7 @@ describe('Note', () => { const resolvedNote = await resolveRemoteNote('a.test', note.id, bob); const emoji = await addCustomEmoji('b.test'); await bob.client.request('notes/reactions/create', { noteId: resolvedNote.id, reaction: `:${emoji.name}:` }); - await sleep(100); + await sleep(); const reactions = await alice.client.request('notes/reactions', { noteId: note.id }); strictEqual(reactions.length, 1); @@ -203,7 +203,7 @@ describe('Note', () => { const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); const emoji = await addCustomEmoji('b.test'); await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction: `:${emoji.name}:` }); - await sleep(100); + await sleep(); const reactions = await alice.client.request('notes/reactions', { noteId: note.id }); strictEqual(reactions.length, 1); @@ -219,7 +219,7 @@ describe('Note', () => { const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); const emoji = await addCustomEmoji('b.test', { isSensitive: true }); await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction: `:${emoji.name}:` }); - await sleep(100); + await sleep(); const reactions = await alice.client.request('notes/reactions', { noteId: note.id }); strictEqual(reactions.length, 1); @@ -240,7 +240,7 @@ describe('Note', () => { const note = (await bob.client.request('notes/create', { poll: { choices: ['inu', 'neko'] } })).createdNote; const noteInAServer = await resolveRemoteNote('b.test', note.id, carol); await carol.client.request('notes/polls/vote', { noteId: noteInAServer.id, choice: 0 }); - await sleep(100); + await sleep(); const noteAfterVote = await bob.client.request('notes/show', { noteId: note.id }); assert(noteAfterVote.poll != null); @@ -262,7 +262,7 @@ describe('Note', () => { ]); await bobRemoteFollower.client.request('following/create', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); }); test('A vote in Bob\'s server is delivered to Bob\'s remote followers', async () => { @@ -270,7 +270,7 @@ describe('Note', () => { // NOTE: resolve before voting const noteInAServer = await resolveRemoteNote('b.test', note.id, bobRemoteFollower); await localVoter.client.request('notes/polls/vote', { noteId: note.id, choice: 0 }); - await sleep(100); + await sleep(); const noteAfterVote = await bobRemoteFollower.client.request('notes/show', { noteId: noteInAServer.id }); assert(noteAfterVote.poll != null); diff --git a/packages/backend/test-federation/test/notification.test.ts b/packages/backend/test-federation/test/notification.test.ts index 1ccd01c8cb4b..c1d0ef5d8149 100644 --- a/packages/backend/test-federation/test/notification.test.ts +++ b/packages/backend/test-federation/test/notification.test.ts @@ -77,7 +77,7 @@ describe('Notification', () => { isFired( 'a.test', alice, 'main', async () => { - await sleep(200); + await sleep(); await bob.client.request('notes/create', { text, replyId: noteInBServer.id }); }, 'notification', msg => { @@ -100,7 +100,7 @@ describe('Notification', () => { isFired( 'a.test', alice, 'main', async () => { - await sleep(200); + await sleep(); await bob.client.request('notes/create', { renoteId: noteInBServer.id }); }, 'notification', msg => { @@ -124,7 +124,7 @@ describe('Notification', () => { isFired( 'a.test', alice, 'main', async () => { - await sleep(200); + await sleep(); await bob.client.request('notes/create', { text, renoteId: noteInBServer.id }); }, 'notification', msg => { @@ -146,7 +146,7 @@ describe('Notification', () => { isFired( 'a.test', alice, 'main', async () => { - await sleep(200); + await sleep(); await bob.client.request('notes/create', { text }); }, 'notification', msg => msg.type === 'mention' && msg.userId === bobInAServer.id && msg.note.text === text, @@ -181,7 +181,7 @@ describe('Notification', () => { alice.client.request('mute/create', { userId: bobInAServer.id }), bob.client.request('mute/create', { userId: aliceInBServer.id }), ]); - await sleep(100); + await sleep(); }); describe('Follow', () => { @@ -191,7 +191,7 @@ describe('Notification', () => { isFired( 'b.test', bob, 'main', async () => { - await sleep(200); + await sleep(); await bob.client.request('following/create', { userId: aliceInBServer.id }); }, 'follow', msg => msg.id === aliceInBServer.id, diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index 423b62cafa25..9f14b733b332 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -20,7 +20,7 @@ describe('Timeline', () => { ]); await bob.client.request('following/create', { userId: aliceInBServer.id }); - await sleep(100); + await sleep(); }); type TimelineChannel = keyof Misskey.Channels & (`${string}Timeline` | 'antenna' | 'userList' | 'hashtag'); @@ -62,7 +62,7 @@ describe('Timeline', () => { endpoint === 'roles/notes' ? { roleId: (channelParams as Misskey.Channels['roleTimeline']['params']).roleId } : {}; - await sleep(100); + await sleep(); const notes = await (bob.client.request as Request)(endpoint, params); const noteInBServer = notes.filter(({ uri }) => uri === `https://a.test/notes/${note!.id}`).pop(); const endpointFired = noteInBServer != null; @@ -77,7 +77,7 @@ describe('Timeline', () => { ); strictEqual(streamingFired, true); - await sleep(100); + await sleep(); const notes = await (bob.client.request as Request)(endpoint, params); const endpointFired = notes.every(({ uri }) => uri !== `https://a.test/notes/${note!.id}`); strictEqual(endpointFired, true); @@ -194,7 +194,7 @@ describe('Timeline', () => { beforeAll(async () => { list = await bob.client.request('users/lists/create', { name: 'Bob\'s List' }); await bob.client.request('users/lists/push', { listId: list.id, userId: aliceInBServer.id }); - await sleep(100); + await sleep(); }); describe('Check reception of remote followee\'s Note', () => { @@ -267,7 +267,7 @@ describe('Timeline', () => { displayOrder: 0, policies: {}, }); - await sleep(100); + await sleep(); }); describe('Check reception of remote followee\'s Note', () => { @@ -311,7 +311,7 @@ describe('Timeline', () => { withReplies: true, withFile: true, }); - await sleep(100); + await sleep(); }); describe('Check reception of remote followee\'s Note', () => { diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index 08948c1b5b73..db48f3b75cb6 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -70,10 +70,10 @@ describe('User', () => { test('Becoming a cat is sent to their followers', async () => { await bob.client.request('following/create', { userId: aliceInBServer.id }); - await sleep(100); + await sleep(); await alice.client.request('i/update', { isCat: true }); - await sleep(100); + await sleep(); const res = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(res.isCat, true); @@ -97,7 +97,7 @@ describe('User', () => { test('Pinning localOnly Note is not delivered', async () => { const note = (await alice.client.request('notes/create', { text: 'a', localOnly: true })).createdNote; await alice.client.request('i/pin', { noteId: note.id }); - await sleep(100); + await sleep(); const _aliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); @@ -106,7 +106,7 @@ describe('User', () => { test('Pinning followers-only Note is not delivered', async () => { const note = (await alice.client.request('notes/create', { text: 'a', visibility: 'followers' })).createdNote; await alice.client.request('i/pin', { noteId: note.id }); - await sleep(100); + await sleep(); const _aliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); @@ -117,7 +117,7 @@ describe('User', () => { test('Pinning normal Note is delivered', async () => { pinnedNote = (await alice.client.request('notes/create', { text: 'a' })).createdNote; await alice.client.request('i/pin', { noteId: pinnedNote.id }); - await sleep(100); + await sleep(); const _aliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 1); @@ -127,7 +127,7 @@ describe('User', () => { test('Unpinning normal Note is delivered', async () => { await alice.client.request('i/unpin', { noteId: pinnedNote.id }); - await sleep(100); + await sleep(); const _aliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); @@ -155,7 +155,7 @@ describe('User', () => { beforeAll(async () => { await alice.client.request('following/create', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); }); test('Check consistency with `users/following` and `users/followers` endpoints', async () => { @@ -178,7 +178,7 @@ describe('User', () => { beforeAll(async () => { await alice.client.request('following/delete', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); }); test('Check consistency with `users/following` and `users/followers` endpoints', async () => { @@ -220,7 +220,7 @@ describe('User', () => { describe('Bob sends follow request to Alice', () => { beforeAll(async () => { await bob.client.request('following/create', { userId: aliceInBServer.id }); - await sleep(100); + await sleep(); }); test('Alice should have a request', async () => { @@ -234,7 +234,7 @@ describe('User', () => { describe('Alice cancels it', () => { beforeAll(async () => { await bob.client.request('following/requests/cancel', { userId: aliceInBServer.id }); - await sleep(100); + await sleep(); }); test('Alice should have no requests', async () => { @@ -247,10 +247,10 @@ describe('User', () => { describe('Send follow request from Bob to Alice and reject', () => { beforeAll(async () => { await bob.client.request('following/create', { userId: aliceInBServer.id }); - await sleep(100); + await sleep(); await alice.client.request('following/requests/reject', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); }); test('Bob should have no requests', async () => { @@ -272,10 +272,10 @@ describe('User', () => { describe('Send follow request from Bob to Alice and accept', () => { beforeAll(async () => { await bob.client.request('following/create', { userId: aliceInBServer.id }); - await sleep(100); + await sleep(); await alice.client.request('following/requests/accept', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); }); test('Bob follows Alice', async () => { @@ -306,13 +306,13 @@ describe('User', () => { test('Bob follows Alice, and Alice deleted themself', async () => { await bob.client.request('following/create', { userId: aliceInBServer.id }); - await sleep(100); + await sleep(); const followers = await alice.client.request('users/followers', { userId: alice.id }); strictEqual(followers.length, 1); // followed by Bob await alice.client.request('i/delete-account', { password: alice.password }); - await sleep(100); + await sleep(); const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 0); // no following relation @@ -345,13 +345,13 @@ describe('User', () => { test('Bob follows Alice, then Alice gets deleted in B server', async () => { await bob.client.request('following/create', { userId: aliceInBServer.id }); - await sleep(100); + await sleep(); const followers = await alice.client.request('users/followers', { userId: alice.id }); strictEqual(followers.length, 1); // followed by Bob await bAdmin.client.request('admin/delete-account', { userId: aliceInBServer.id }); - await sleep(100); + await sleep(); // TODO: why still following relation? const following = await bob.client.request('users/following', { userId: bob.id }); @@ -367,7 +367,7 @@ describe('User', () => { test('Alice tries to follow Bob, but it is not processed', async () => { await alice.client.request('following/create', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); const following = await alice.client.request('users/following', { userId: alice.id }); strictEqual(following.length, 0); // Not following Bob because B server doesn't return Accept @@ -397,13 +397,13 @@ describe('User', () => { test('Bob follows Alice, and Alice gets suspended, there is no following relation, and Bob fails to follow again', async () => { await bob.client.request('following/create', { userId: aliceInBServer.id }); - await sleep(100); + await sleep(); const followers = await alice.client.request('users/followers', { userId: alice.id }); strictEqual(followers.length, 1); // followed by Bob await aAdmin.client.request('admin/suspend-user', { userId: alice.id }); - await sleep(100); + await sleep(); const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 0); // no following relation @@ -419,7 +419,7 @@ describe('User', () => { test('Alice gets unsuspended, Bob succeeds in following Alice', async () => { await aAdmin.client.request('admin/unsuspend-user', { userId: alice.id }); - await sleep(100); + await sleep(); const followers = await alice.client.request('users/followers', { userId: alice.id }); strictEqual(followers.length, 1); // FIXME: followers are not deleted?? @@ -452,7 +452,7 @@ describe('User', () => { */ test('Alice can follow Bob', async () => { await alice.client.request('following/create', { userId: bobInAServer.id }); - await sleep(100); + await sleep(); const bobFollowers = await bob.client.request('users/followers', { userId: bob.id }); strictEqual(bobFollowers.length, 1); // followed by Alice @@ -467,7 +467,7 @@ describe('User', () => { // Bob tries to follow Alice await bob.client.request('following/create', { userId: renewedAliceInBServer.id }); - await sleep(100); + await sleep(); const aliceFollowers = await alice.client.request('users/followers', { userId: alice.id }); strictEqual(aliceFollowers.length, 1); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 31ffac406c2c..88d3906bc328 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -34,7 +34,7 @@ export type Request = { +export async function sleep(ms = 200): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } From 2b886617f96014f6fa07c07cf64cce3cf0e1802b Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 23:35:48 +0900 Subject: [PATCH 087/111] fix(notification): omit flaky tests --- .../test-federation/test/notification.test.ts | 100 ++++++------------ 1 file changed, 30 insertions(+), 70 deletions(-) diff --git a/packages/backend/test-federation/test/notification.test.ts b/packages/backend/test-federation/test/notification.test.ts index c1d0ef5d8149..1d42be6fea07 100644 --- a/packages/backend/test-federation/test/notification.test.ts +++ b/packages/backend/test-federation/test/notification.test.ts @@ -73,91 +73,51 @@ describe('Notification', () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); const text = crypto.randomUUID(); - const fired = await Promise.all([ - isFired( - 'a.test', alice, 'main', - async () => { - await sleep(); - await bob.client.request('notes/create', { text, replyId: noteInBServer.id }); - }, - 'notification', msg => { - return msg.type === 'reply' && msg.note.reply!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; - }, - ), - isFired( - 'a.test', alice, 'main', - async () => {}, // NOTE: do nothing because already done above - 'reply', msg => msg.reply!.id === note.id && msg.userId === bobInAServer.id && msg.text === text, - ), - ]); - deepStrictEqual(fired, [true, true]); + const fired = await isFired( + 'a.test', alice, 'main', + async () => await bob.client.request('notes/create', { text, replyId: noteInBServer.id }), + 'notification', msg => { + return msg.type === 'reply' && msg.note.reply!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; + }, + ); + deepStrictEqual(fired, true); }); test('Get notification when renoted', async () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - const fired = await Promise.all([ - isFired( - 'a.test', alice, 'main', - async () => { - await sleep(); - await bob.client.request('notes/create', { renoteId: noteInBServer.id }); - }, - 'notification', msg => { - return msg.type === 'renote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id; - }, - ), - isFired( - 'a.test', alice, 'main', - async () => {}, // NOTE: do nothing because already done above - 'renote', msg => msg.renote!.id === note.id && msg.userId === bobInAServer.id, - ), - ]); - deepStrictEqual(fired, [true, true]); + const fired = await isFired( + 'a.test', alice, 'main', + async () => await bob.client.request('notes/create', { renoteId: noteInBServer.id }), + 'notification', msg => { + return msg.type === 'renote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id; + }, + ); + deepStrictEqual(fired, true); }); test('Get notification when quoted', async () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); const text = crypto.randomUUID(); - const fired = await Promise.all([ - isFired( - 'a.test', alice, 'main', - async () => { - await sleep(); - await bob.client.request('notes/create', { text, renoteId: noteInBServer.id }); - }, - 'notification', msg => { - return msg.type === 'quote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; - }, - ), - isFired( - 'a.test', alice, 'main', - async () => {}, // NOTE: do nothing because already done above - 'renote', msg => msg.renote!.id === note.id && msg.userId === bobInAServer.id && msg.text === text, - ), - ]); - deepStrictEqual(fired, [true, true]); + const fired = await isFired( + 'a.test', alice, 'main', + async () => await bob.client.request('notes/create', { text, renoteId: noteInBServer.id }), + 'notification', msg => { + return msg.type === 'quote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; + }, + ); + deepStrictEqual(fired, true); }); test('Get notification when mentioned', async () => { const text = `@${alice.username}@a.test`; - const fired = await Promise.all([ - isFired( - 'a.test', alice, 'main', - async () => { - await sleep(); - await bob.client.request('notes/create', { text }); - }, - 'notification', msg => msg.type === 'mention' && msg.userId === bobInAServer.id && msg.note.text === text, - ), - isFired( - 'a.test', alice, 'main', - async () => {}, // NOTE: do nothing because already done above - 'mention', msg => msg.userId === bobInAServer.id && msg.text === text, - ), - ]); - deepStrictEqual(fired, [true, true]); + const fired = await isFired( + 'a.test', alice, 'main', + async () => await bob.client.request('notes/create', { text }), + 'notification', msg => msg.type === 'mention' && msg.userId === bobInAServer.id && msg.note.text === text, + ); + deepStrictEqual(fired, true); }); }); }); From cfa764dbf62c1bb9925d2f5811cc48ebb2bfe797 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 25 Sep 2024 23:40:38 +0900 Subject: [PATCH 088/111] fix(notification): correct type --- .../test-federation/test/notification.test.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/backend/test-federation/test/notification.test.ts b/packages/backend/test-federation/test/notification.test.ts index 1d42be6fea07..512bbf41ace7 100644 --- a/packages/backend/test-federation/test/notification.test.ts +++ b/packages/backend/test-federation/test/notification.test.ts @@ -26,12 +26,12 @@ describe('Notification', () => { isFired( 'b.test', bob, 'main', async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), - 'follow', msg => msg.id === aliceInBServer.id, + 'notification', msg => msg.type === 'followRequestAccepted' && msg.userId === aliceInBServer.id, ), isFired( 'a.test', alice, 'main', async () => {}, // NOTE: do nothing because done in above - 'followed', msg => msg.id === bobInAServer.id, + 'notification', msg => msg.type === 'follow' && msg.userId === bobInAServer.id, ), ]); deepStrictEqual(fired, [true, true]); @@ -145,8 +145,7 @@ describe('Notification', () => { }); describe('Follow', () => { - // NOTE: you cannot mute follow/followed notification - test('Get notification when follow/followed', async () => { + test('Get no notification when follow/followed', async () => { const fired = await Promise.all([ isFired( 'b.test', bob, 'main', @@ -154,15 +153,15 @@ describe('Notification', () => { await sleep(); await bob.client.request('following/create', { userId: aliceInBServer.id }); }, - 'follow', msg => msg.id === aliceInBServer.id, + 'notification', msg => msg.type === 'followRequestAccepted' && msg.userId === aliceInBServer.id, ), isFired( 'a.test', alice, 'main', async () => {}, // NOTE: do nothing because done in above - 'followed', msg => msg.id === bobInAServer.id, + 'notification', msg => msg.type === 'follow' && msg.userId === bobInAServer.id, ), ]); - deepStrictEqual(fired, [true, true]); + deepStrictEqual(fired, [false, false]); }); afterAll(async () => await bob.client.request('following/delete', { userId: aliceInBServer.id })); From 373401e399e1590dfe448e8319d39e4576ffc9a6 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 26 Sep 2024 00:10:57 +0900 Subject: [PATCH 089/111] test(notification): add api endpoint tests --- .../test-federation/test/notification.test.ts | 191 ++++++++---------- .../backend/test-federation/test/utils.ts | 18 ++ 2 files changed, 105 insertions(+), 104 deletions(-) diff --git a/packages/backend/test-federation/test/notification.test.ts b/packages/backend/test-federation/test/notification.test.ts index 512bbf41ace7..e904282018f8 100644 --- a/packages/backend/test-federation/test/notification.test.ts +++ b/packages/backend/test-federation/test/notification.test.ts @@ -1,8 +1,6 @@ -import { deepStrictEqual, strictEqual } from 'assert'; import * as Misskey from 'misskey-js'; -import { createAccount, isFired, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; +import { assertNotificationReceived, createAccount, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; -// TODO: these tests only checks streaming, so API endpoints are not tested describe('Notification', () => { describe('Non-mute', () => { let alice: LoginUser, bob: LoginUser; @@ -21,34 +19,25 @@ describe('Notification', () => { }); describe('Follow', () => { - test('Get notification when follow/followed', async () => { - const fired = await Promise.all([ - isFired( - 'b.test', bob, 'main', - async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), - 'notification', msg => msg.type === 'followRequestAccepted' && msg.userId === aliceInBServer.id, - ), - isFired( - 'a.test', alice, 'main', - async () => {}, // NOTE: do nothing because done in above - 'notification', msg => msg.type === 'follow' && msg.userId === bobInAServer.id, - ), - ]); - deepStrictEqual(fired, [true, true]); - - { - const notifications = await bob.client.request('i/notifications', {}); - const notification = notifications[0]; - strictEqual(notification.type, 'followRequestAccepted'); - strictEqual(notification.userId, aliceInBServer.id); - } - - { - const notifications = await alice.client.request('i/notifications', {}); - const notification = notifications[0]; - strictEqual(notification.type, 'follow'); - strictEqual(notification.userId, bobInAServer.id); - } + test('Get notification when follow', async () => { + await assertNotificationReceived( + 'b.test', bob, + async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + notification => notification.type === 'followRequestAccepted' && notification.userId === aliceInBServer.id, + true, + ); + + await bob.client.request('following/delete', { userId: aliceInBServer.id }); + await sleep(); + }); + + test('Get notification when get followed', async () => { + await assertNotificationReceived( + 'a.test', alice, + async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + notification => notification.type === 'follow' && notification.userId === bobInAServer.id, + true, + ); }); afterAll(async () => await bob.client.request('following/delete', { userId: aliceInBServer.id })); @@ -59,65 +48,61 @@ describe('Notification', () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); const reaction = '😅'; - const fired = await isFired( - 'a.test', alice, 'main', + await assertNotificationReceived( + 'a.test', alice, async () => await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction }), - 'notification', msg => { - return msg.type === 'reaction' && msg.note.id === note.id && msg.userId === bobInAServer.id && msg.reaction === reaction; - }, + notification => + notification.type === 'reaction' && notification.note.id === note.id && notification.userId === bobInAServer.id && notification.reaction === reaction, + true, ); - strictEqual(fired, true); }); test('Get notification when replied', async () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); const text = crypto.randomUUID(); - const fired = await isFired( - 'a.test', alice, 'main', + await assertNotificationReceived( + 'a.test', alice, async () => await bob.client.request('notes/create', { text, replyId: noteInBServer.id }), - 'notification', msg => { - return msg.type === 'reply' && msg.note.reply!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; - }, + notification => + notification.type === 'reply' && notification.note.reply!.id === note.id && notification.userId === bobInAServer.id && notification.note.text === text, + true, ); - deepStrictEqual(fired, true); }); test('Get notification when renoted', async () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - const fired = await isFired( - 'a.test', alice, 'main', + await assertNotificationReceived( + 'a.test', alice, async () => await bob.client.request('notes/create', { renoteId: noteInBServer.id }), - 'notification', msg => { - return msg.type === 'renote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id; - }, + notification => + notification.type === 'renote' && notification.note.renote!.id === note.id && notification.userId === bobInAServer.id, + true, ); - deepStrictEqual(fired, true); }); test('Get notification when quoted', async () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); const text = crypto.randomUUID(); - const fired = await isFired( - 'a.test', alice, 'main', + await assertNotificationReceived( + 'a.test', alice, async () => await bob.client.request('notes/create', { text, renoteId: noteInBServer.id }), - 'notification', msg => { - return msg.type === 'quote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; - }, + notification => + notification.type === 'quote' && notification.note.renote!.id === note.id && notification.userId === bobInAServer.id && notification.note.text === text, + true, ); - deepStrictEqual(fired, true); }); test('Get notification when mentioned', async () => { const text = `@${alice.username}@a.test`; - const fired = await isFired( - 'a.test', alice, 'main', + await assertNotificationReceived( + 'a.test', alice, async () => await bob.client.request('notes/create', { text }), - 'notification', msg => msg.type === 'mention' && msg.userId === bobInAServer.id && msg.note.text === text, + notification => notification.type === 'mention' && notification.userId === bobInAServer.id && notification.note.text === text, + true, ); - deepStrictEqual(fired, true); }); }); }); @@ -145,23 +130,25 @@ describe('Notification', () => { }); describe('Follow', () => { - test('Get no notification when follow/followed', async () => { - const fired = await Promise.all([ - isFired( - 'b.test', bob, 'main', - async () => { - await sleep(); - await bob.client.request('following/create', { userId: aliceInBServer.id }); - }, - 'notification', msg => msg.type === 'followRequestAccepted' && msg.userId === aliceInBServer.id, - ), - isFired( - 'a.test', alice, 'main', - async () => {}, // NOTE: do nothing because done in above - 'notification', msg => msg.type === 'follow' && msg.userId === bobInAServer.id, - ), - ]); - deepStrictEqual(fired, [false, false]); + test('Get no notification when follow', async () => { + await assertNotificationReceived( + 'b.test', bob, + async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + notification => notification.type === 'followRequestAccepted' && notification.userId === aliceInBServer.id, + false, + ); + + await bob.client.request('following/delete', { userId: aliceInBServer.id }); + await sleep(); + }); + + test('Get notification when get followed', async () => { + await assertNotificationReceived( + 'a.test', alice, + async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + notification => notification.type === 'follow' && notification.userId === bobInAServer.id, + false, + ); }); afterAll(async () => await bob.client.request('following/delete', { userId: aliceInBServer.id })); @@ -172,65 +159,61 @@ describe('Notification', () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); const reaction = '😅'; - const fired = await isFired( - 'a.test', alice, 'main', + await assertNotificationReceived( + 'a.test', alice, async () => await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction }), - 'notification', msg => { - return msg.type === 'reaction' && msg.note.id === note.id && msg.userId === bobInAServer.id && msg.reaction === reaction; - }, + notification => + notification.type === 'reaction' && notification.note.id === note.id && notification.userId === bobInAServer.id && notification.reaction === reaction, + false, ); - strictEqual(fired, false); }); test('Get no notification when replied', async () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); const text = crypto.randomUUID(); - const fired = await isFired( - 'a.test', alice, 'main', + await assertNotificationReceived( + 'a.test', alice, async () => await bob.client.request('notes/create', { text, replyId: noteInBServer.id }), - 'notification', msg => { - return msg.type === 'reply' && msg.note.reply!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; - }, + notification => + notification.type === 'reply' && notification.note.reply!.id === note.id && notification.userId === bobInAServer.id && notification.note.text === text, + false, ); - deepStrictEqual(fired, false); }); test('Get no notification when renoted', async () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - const fired = await isFired( - 'a.test', alice, 'main', + await assertNotificationReceived( + 'a.test', alice, async () => await bob.client.request('notes/create', { renoteId: noteInBServer.id }), - 'notification', msg => { - return msg.type === 'renote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id; - }, + notification => + notification.type === 'renote' && notification.note.renote!.id === note.id && notification.userId === bobInAServer.id, + false, ); - deepStrictEqual(fired, false); }); test('Get no notification when quoted', async () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); const text = crypto.randomUUID(); - const fired = await isFired( - 'a.test', alice, 'main', + await assertNotificationReceived( + 'a.test', alice, async () => await bob.client.request('notes/create', { text, renoteId: noteInBServer.id }), - 'notification', msg => { - return msg.type === 'quote' && msg.note.renote!.id === note.id && msg.userId === bobInAServer.id && msg.note.text === text; - }, + notification => + notification.type === 'quote' && notification.note.renote!.id === note.id && notification.userId === bobInAServer.id && notification.note.text === text, + false, ); - deepStrictEqual(fired, false); }); test('Get no notification when mentioned', async () => { const text = `@${alice.username}@a.test`; - const fired = await isFired( - 'a.test', alice, 'main', + await assertNotificationReceived( + 'a.test', alice, async () => await bob.client.request('notes/create', { text }), - 'notification', msg => msg.type === 'mention' && msg.userId === bobInAServer.id && msg.note.text === text, + notification => notification.type === 'mention' && notification.userId === bobInAServer.id && notification.note.text === text, + false, ); - deepStrictEqual(fired, false); }); }); }); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 88d3906bc328..b7d9490be1e8 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -256,3 +256,21 @@ export async function isNoteUpdatedEventFired( }); }); }; + +export async function assertNotificationReceived( + receiverHost: Host, + receiver: { i: string }, + trigger: () => Promise, + cond: (notification: Misskey.entities.Notification) => boolean, + expect: boolean, +) { + const streamingFired = await isFired(receiverHost, receiver, 'main', trigger, 'notification', cond); + strictEqual(streamingFired, expect); + + const endpointFired = await new Misskey.api.APIClient({ + origin: `https://${receiverHost}`, credential: receiver.i, + }).request('i/notifications', {}) + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + .then(([notification]) => notification != null ? cond(notification) : false); + strictEqual(endpointFired, expect); +} From fb09f4ef526134e6f892b4fd9f48bef6e53eb0b0 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 26 Sep 2024 00:14:15 +0900 Subject: [PATCH 090/111] chore: remove redundant mute test --- .../test-federation/test/notification.test.ts | 285 ++++++------------ 1 file changed, 86 insertions(+), 199 deletions(-) diff --git a/packages/backend/test-federation/test/notification.test.ts b/packages/backend/test-federation/test/notification.test.ts index e904282018f8..52f43f4e67b0 100644 --- a/packages/backend/test-federation/test/notification.test.ts +++ b/packages/backend/test-federation/test/notification.test.ts @@ -2,219 +2,106 @@ import * as Misskey from 'misskey-js'; import { assertNotificationReceived, createAccount, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; describe('Notification', () => { - describe('Non-mute', () => { - let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let alice: LoginUser, bob: LoginUser; + let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [bobInAServer, aliceInBServer] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), + ]); + }); - beforeAll(async () => { - [alice, bob] = await Promise.all([ - createAccount('a.test'), - createAccount('b.test'), - ]); + describe('Follow', () => { + test('Get notification when follow', async () => { + await assertNotificationReceived( + 'b.test', bob, + async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + notification => notification.type === 'followRequestAccepted' && notification.userId === aliceInBServer.id, + true, + ); - [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, alice), - resolveRemoteUser('a.test', alice.id, bob), - ]); + await bob.client.request('following/delete', { userId: aliceInBServer.id }); + await sleep(); }); - describe('Follow', () => { - test('Get notification when follow', async () => { - await assertNotificationReceived( - 'b.test', bob, - async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), - notification => notification.type === 'followRequestAccepted' && notification.userId === aliceInBServer.id, - true, - ); - - await bob.client.request('following/delete', { userId: aliceInBServer.id }); - await sleep(); - }); - - test('Get notification when get followed', async () => { - await assertNotificationReceived( - 'a.test', alice, - async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), - notification => notification.type === 'follow' && notification.userId === bobInAServer.id, - true, - ); - }); - - afterAll(async () => await bob.client.request('following/delete', { userId: aliceInBServer.id })); + test('Get notification when get followed', async () => { + await assertNotificationReceived( + 'a.test', alice, + async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + notification => notification.type === 'follow' && notification.userId === bobInAServer.id, + true, + ); }); - describe('Note', () => { - test('Get notification when get a reaction', async () => { - const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - const reaction = '😅'; - await assertNotificationReceived( - 'a.test', alice, - async () => await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction }), - notification => - notification.type === 'reaction' && notification.note.id === note.id && notification.userId === bobInAServer.id && notification.reaction === reaction, - true, - ); - }); - - test('Get notification when replied', async () => { - const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - const text = crypto.randomUUID(); - await assertNotificationReceived( - 'a.test', alice, - async () => await bob.client.request('notes/create', { text, replyId: noteInBServer.id }), - notification => - notification.type === 'reply' && notification.note.reply!.id === note.id && notification.userId === bobInAServer.id && notification.note.text === text, - true, - ); - }); - - test('Get notification when renoted', async () => { - const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - await assertNotificationReceived( - 'a.test', alice, - async () => await bob.client.request('notes/create', { renoteId: noteInBServer.id }), - notification => - notification.type === 'renote' && notification.note.renote!.id === note.id && notification.userId === bobInAServer.id, - true, - ); - }); - - test('Get notification when quoted', async () => { - const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - const text = crypto.randomUUID(); - await assertNotificationReceived( - 'a.test', alice, - async () => await bob.client.request('notes/create', { text, renoteId: noteInBServer.id }), - notification => - notification.type === 'quote' && notification.note.renote!.id === note.id && notification.userId === bobInAServer.id && notification.note.text === text, - true, - ); - }); - - test('Get notification when mentioned', async () => { - const text = `@${alice.username}@a.test`; - await assertNotificationReceived( - 'a.test', alice, - async () => await bob.client.request('notes/create', { text }), - notification => notification.type === 'mention' && notification.userId === bobInAServer.id && notification.note.text === text, - true, - ); - }); - }); + afterAll(async () => await bob.client.request('following/delete', { userId: aliceInBServer.id })); }); - describe('Mute', () => { - let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; - - beforeAll(async () => { - [alice, bob] = await Promise.all([ - createAccount('a.test'), - createAccount('b.test'), - ]); - - [bobInAServer, aliceInBServer] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, alice), - resolveRemoteUser('a.test', alice.id, bob), - ]); - - await Promise.all([ - alice.client.request('mute/create', { userId: bobInAServer.id }), - bob.client.request('mute/create', { userId: aliceInBServer.id }), - ]); - await sleep(); + describe('Note', () => { + test('Get notification when get a reaction', async () => { + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const reaction = '😅'; + await assertNotificationReceived( + 'a.test', alice, + async () => await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction }), + notification => + notification.type === 'reaction' && notification.note.id === note.id && notification.userId === bobInAServer.id && notification.reaction === reaction, + true, + ); }); - describe('Follow', () => { - test('Get no notification when follow', async () => { - await assertNotificationReceived( - 'b.test', bob, - async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), - notification => notification.type === 'followRequestAccepted' && notification.userId === aliceInBServer.id, - false, - ); - - await bob.client.request('following/delete', { userId: aliceInBServer.id }); - await sleep(); - }); - - test('Get notification when get followed', async () => { - await assertNotificationReceived( - 'a.test', alice, - async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), - notification => notification.type === 'follow' && notification.userId === bobInAServer.id, - false, - ); - }); - - afterAll(async () => await bob.client.request('following/delete', { userId: aliceInBServer.id })); + test('Get notification when replied', async () => { + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const text = crypto.randomUUID(); + await assertNotificationReceived( + 'a.test', alice, + async () => await bob.client.request('notes/create', { text, replyId: noteInBServer.id }), + notification => + notification.type === 'reply' && notification.note.reply!.id === note.id && notification.userId === bobInAServer.id && notification.note.text === text, + true, + ); }); - describe('Note', () => { - test('Get no notification when get a reaction', async () => { - const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - const reaction = '😅'; - await assertNotificationReceived( - 'a.test', alice, - async () => await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction }), - notification => - notification.type === 'reaction' && notification.note.id === note.id && notification.userId === bobInAServer.id && notification.reaction === reaction, - false, - ); - }); - - test('Get no notification when replied', async () => { - const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - const text = crypto.randomUUID(); - await assertNotificationReceived( - 'a.test', alice, - async () => await bob.client.request('notes/create', { text, replyId: noteInBServer.id }), - notification => - notification.type === 'reply' && notification.note.reply!.id === note.id && notification.userId === bobInAServer.id && notification.note.text === text, - false, - ); - }); - - test('Get no notification when renoted', async () => { - const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - await assertNotificationReceived( - 'a.test', alice, - async () => await bob.client.request('notes/create', { renoteId: noteInBServer.id }), - notification => - notification.type === 'renote' && notification.note.renote!.id === note.id && notification.userId === bobInAServer.id, - false, - ); - }); + test('Get notification when renoted', async () => { + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + await assertNotificationReceived( + 'a.test', alice, + async () => await bob.client.request('notes/create', { renoteId: noteInBServer.id }), + notification => + notification.type === 'renote' && notification.note.renote!.id === note.id && notification.userId === bobInAServer.id, + true, + ); + }); - test('Get no notification when quoted', async () => { - const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - const text = crypto.randomUUID(); - await assertNotificationReceived( - 'a.test', alice, - async () => await bob.client.request('notes/create', { text, renoteId: noteInBServer.id }), - notification => - notification.type === 'quote' && notification.note.renote!.id === note.id && notification.userId === bobInAServer.id && notification.note.text === text, - false, - ); - }); + test('Get notification when quoted', async () => { + const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const text = crypto.randomUUID(); + await assertNotificationReceived( + 'a.test', alice, + async () => await bob.client.request('notes/create', { text, renoteId: noteInBServer.id }), + notification => + notification.type === 'quote' && notification.note.renote!.id === note.id && notification.userId === bobInAServer.id && notification.note.text === text, + true, + ); + }); - test('Get no notification when mentioned', async () => { - const text = `@${alice.username}@a.test`; - await assertNotificationReceived( - 'a.test', alice, - async () => await bob.client.request('notes/create', { text }), - notification => notification.type === 'mention' && notification.userId === bobInAServer.id && notification.note.text === text, - false, - ); - }); + test('Get notification when mentioned', async () => { + const text = `@${alice.username}@a.test`; + await assertNotificationReceived( + 'a.test', alice, + async () => await bob.client.request('notes/create', { text }), + notification => notification.type === 'mention' && notification.userId === bobInAServer.id && notification.note.text === text, + true, + ); }); }); }); From 9c2d6b23c16a18f3019b01dc53a1b9ce05217451 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 26 Sep 2024 00:19:40 +0900 Subject: [PATCH 091/111] refactor: use param client --- packages/backend/test-federation/test/utils.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index b7d9490be1e8..984801f97b19 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -259,7 +259,7 @@ export async function isNoteUpdatedEventFired( export async function assertNotificationReceived( receiverHost: Host, - receiver: { i: string }, + receiver: LoginUser, trigger: () => Promise, cond: (notification: Misskey.entities.Notification) => boolean, expect: boolean, @@ -267,9 +267,7 @@ export async function assertNotificationReceived( const streamingFired = await isFired(receiverHost, receiver, 'main', trigger, 'notification', cond); strictEqual(streamingFired, expect); - const endpointFired = await new Misskey.api.APIClient({ - origin: `https://${receiverHost}`, credential: receiver.i, - }).request('i/notifications', {}) + const endpointFired = await receiver.client.request('i/notifications', {}) // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition .then(([notification]) => notification != null ? cond(notification) : false); strictEqual(endpointFired, expect); From 72c9e09abdb116ab59a8f5dd76d1e287e765a755 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 26 Sep 2024 00:23:14 +0900 Subject: [PATCH 092/111] fix: start timer after trigger --- packages/backend/test-federation/test/utils.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 984801f97b19..242a3ea5066a 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -210,12 +210,14 @@ export async function isFired { - stream.close(); - resolve(false); - }, 1000); + let timer: NodeJS.Timeout | undefined; - await trigger().catch(err => { + await trigger().then(() => { + timer = setTimeout(() => { + stream.close(); + resolve(false); + }, 1000); + }).catch(err => { stream.close(); clearTimeout(timer); reject(err); From ad5a4facbbf7923566c4a0cf75eda12f2d734b6c Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 26 Sep 2024 00:25:09 +0900 Subject: [PATCH 093/111] refactor: remove unnecessary any --- packages/backend/test-federation/test/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 242a3ea5066a..b89b89bf50e2 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -236,13 +236,13 @@ export async function isNoteUpdatedEventFired( // @ts-expect-error TODO: why? const stream = new Misskey.Stream(`wss://${host}`, { token: user.i }, { WebSocket }); stream.send('s', { id: noteId }); - stream.on('noteUpdated', ((msg: any) => { + stream.on('noteUpdated', msg => { if (cond(msg)) { stream.close(); clearTimeout(timer); resolve(true); } - }) as any); + }); let timer: NodeJS.Timeout | undefined; From aa67eb12a28b7f4165a3ba371b66e8709c697bb3 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 26 Sep 2024 00:29:31 +0900 Subject: [PATCH 094/111] chore: shorter timeout for checking if fired --- packages/backend/test-federation/test/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index b89b89bf50e2..c5ec40340cdb 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -216,7 +216,7 @@ export async function isFired { stream.close(); resolve(false); - }, 1000); + }, 500); }).catch(err => { stream.close(); clearTimeout(timer); @@ -250,7 +250,7 @@ export async function isNoteUpdatedEventFired( timer = setTimeout(() => { stream.close(); resolve(false); - }, 1000); + }, 500); }).catch(err => { stream.close(); clearTimeout(timer); From da53aa699dff59d982774e73fbb43da330a43d0d Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:15:40 +0900 Subject: [PATCH 095/111] fix(block): remove outdated comment --- packages/backend/test-federation/test/block.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test-federation/test/block.test.ts b/packages/backend/test-federation/test/block.test.ts index 1e383d470df9..246737b1ffcb 100644 --- a/packages/backend/test-federation/test/block.test.ts +++ b/packages/backend/test-federation/test/block.test.ts @@ -207,7 +207,7 @@ describe('Block', () => { ]); }); - /** NOTE: You should mute the target to stop receiving notifications for now. See {@link file://./notification.test.ts} */ + /** NOTE: You should mute the target to stop receiving notifications */ test('Can mention and notified even if blocked', async () => { await alice.client.request('blocking/create', { userId: bobInAServer.id }); await sleep(); From ca5e403d9ec7cfff74056f6757e8710be0f085e7 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:20:31 +0900 Subject: [PATCH 096/111] refactor: shorten remote user variable name --- .../test-federation/test/block.test.ts | 44 +++--- .../test-federation/test/drive.test.ts | 76 +++++----- .../test-federation/test/emoji.test.ts | 50 +++---- .../backend/test-federation/test/note.test.ts | 38 ++--- .../test-federation/test/notification.test.ts | 42 +++--- .../test-federation/test/timeline.test.ts | 32 ++--- .../backend/test-federation/test/user.test.ts | 132 +++++++++--------- 7 files changed, 207 insertions(+), 207 deletions(-) diff --git a/packages/backend/test-federation/test/block.test.ts b/packages/backend/test-federation/test/block.test.ts index 246737b1ffcb..1aea48ed246c 100644 --- a/packages/backend/test-federation/test/block.test.ts +++ b/packages/backend/test-federation/test/block.test.ts @@ -5,7 +5,7 @@ import { createAccount, isFired, type LoginUser, resolveRemoteNote, resolveRemot describe('Block', () => { describe('Check follow', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -13,17 +13,17 @@ describe('Block', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); }); test('Cannot follow if blocked', async () => { - await alice.client.request('blocking/create', { userId: bobInAServer.id }); + await alice.client.request('blocking/create', { userId: bobInA.id }); await sleep(); await rejects( - async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + async () => await bob.client.request('following/create', { userId: aliceInB.id }), (err: any) => { strictEqual(err.code, 'BLOCKED'); return true; @@ -39,12 +39,12 @@ describe('Block', () => { // FIXME: this is invalid case test('Cannot follow even if unblocked', async () => { // unblock here - await alice.client.request('blocking/delete', { userId: bobInAServer.id }); + await alice.client.request('blocking/delete', { userId: bobInA.id }); await sleep(); // TODO: why still being blocked? await rejects( - async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + async () => await bob.client.request('following/create', { userId: aliceInB.id }), (err: any) => { strictEqual(err.code, 'BLOCKED'); return true; @@ -53,10 +53,10 @@ describe('Block', () => { }); test.skip('Can follow if unblocked', async () => { - await alice.client.request('blocking/delete', { userId: bobInAServer.id }); + await alice.client.request('blocking/delete', { userId: bobInA.id }); await sleep(); - await bob.client.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInB.id }); await sleep(); const following = await bob.client.request('users/following', { userId: bob.id }); @@ -73,7 +73,7 @@ describe('Block', () => { strictEqual(followers.length, 1); }); - await alice.client.request('blocking/create', { userId: bobInAServer.id }); + await alice.client.request('blocking/create', { userId: bobInA.id }); await sleep(); test('after block', async () => { @@ -87,7 +87,7 @@ describe('Block', () => { describe('Check reply', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -95,14 +95,14 @@ describe('Block', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); }); test('Cannot reply if blocked', async () => { - await alice.client.request('blocking/create', { userId: bobInAServer.id }); + await alice.client.request('blocking/create', { userId: bobInA.id }); await sleep(); const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; @@ -117,7 +117,7 @@ describe('Block', () => { }); test('Can reply if unblocked', async () => { - await alice.client.request('blocking/delete', { userId: bobInAServer.id }); + await alice.client.request('blocking/delete', { userId: bobInA.id }); await sleep(); const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; @@ -130,7 +130,7 @@ describe('Block', () => { describe('Check reaction', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -138,14 +138,14 @@ describe('Block', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); }); test('Cannot reaction if blocked', async () => { - await alice.client.request('blocking/create', { userId: bobInAServer.id }); + await alice.client.request('blocking/create', { userId: bobInA.id }); await sleep(); const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; @@ -162,7 +162,7 @@ describe('Block', () => { // FIXME: this is invalid case test('Cannot reaction even if unblocked', async () => { // unblock here - await alice.client.request('blocking/delete', { userId: bobInAServer.id }); + await alice.client.request('blocking/delete', { userId: bobInA.id }); await sleep(); const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; @@ -179,7 +179,7 @@ describe('Block', () => { }); test.skip('Can reaction if unblocked', async () => { - await alice.client.request('blocking/delete', { userId: bobInAServer.id }); + await alice.client.request('blocking/delete', { userId: bobInA.id }); await sleep(); const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; @@ -193,7 +193,7 @@ describe('Block', () => { describe('Check mention', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -201,7 +201,7 @@ describe('Block', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); @@ -209,7 +209,7 @@ describe('Block', () => { /** NOTE: You should mute the target to stop receiving notifications */ test('Can mention and notified even if blocked', async () => { - await alice.client.request('blocking/create', { userId: bobInAServer.id }); + await alice.client.request('blocking/create', { userId: bobInA.id }); await sleep(); const text = `@${alice.username}@a.test plz unblock me!`; @@ -219,7 +219,7 @@ describe('Block', () => { await sleep(); await bob.client.request('notes/create', { text }); }, - 'notification', msg => msg.type === 'mention' && msg.userId === bobInAServer.id && msg.note.text === text, + 'notification', msg => msg.type === 'mention' && msg.userId === bobInA.id && msg.note.text === text, ); deepStrictEqual(fired, true); }); diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index 39325ed88d45..0100f292527e 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -12,23 +12,23 @@ describe('Drive', () => { uploader = await createAccount('a.test'); }); - let image: Misskey.entities.DriveFile, imageInBServer: Misskey.entities.DriveFile; + let image: Misskey.entities.DriveFile, imageInB: Misskey.entities.DriveFile; describe('Upload', () => { beforeAll(async () => { image = await uploadFile('a.test', '../../test/resources/192.jpg', uploader.i); const noteWithImage = (await uploader.client.request('notes/create', { fileIds: [image.id] })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', noteWithImage.id, bAdmin); - assert(noteInBServer.files != null); - strictEqual(noteInBServer.files.length, 1); - imageInBServer = noteInBServer.files[0]; + const noteInB = await resolveRemoteNote('a.test', noteWithImage.id, bAdmin); + assert(noteInB.files != null); + strictEqual(noteInB.files.length, 1); + imageInB = noteInB.files[0]; }); test('Check consistency of DriveFile', () => { // console.log(`a.test: ${JSON.stringify(image, null, '\t')}`); - // console.log(`b.test: ${JSON.stringify(imageInBServer, null, '\t')}`); + // console.log(`b.test: ${JSON.stringify(imageInB, null, '\t')}`); - deepStrictEqualWithExcludedFields(image, imageInBServer, [ + deepStrictEqualWithExcludedFields(image, imageInB, [ 'id', 'createdAt', 'size', @@ -39,7 +39,7 @@ describe('Drive', () => { }); }); - let updatedImage: Misskey.entities.DriveFile, updatedImageInBServer: Misskey.entities.DriveFile; + let updatedImage: Misskey.entities.DriveFile, updatedImageInB: Misskey.entities.DriveFile; describe('Update', () => { beforeAll(async () => { @@ -49,41 +49,41 @@ describe('Drive', () => { isSensitive: true, }); - updatedImageInBServer = await bAdmin.client.request('drive/files/show', { - fileId: imageInBServer.id, + updatedImageInB = await bAdmin.client.request('drive/files/show', { + fileId: imageInB.id, }); }); test('Check consistency', () => { // console.log(`a.test: ${JSON.stringify(updatedImage, null, '\t')}`); - // console.log(`b.test: ${JSON.stringify(updatedImageInBServer, null, '\t')}`); + // console.log(`b.test: ${JSON.stringify(updatedImageInB, null, '\t')}`); // FIXME: not updated with `drive/files/update` strictEqual(updatedImage.isSensitive, true); strictEqual(updatedImage.name, 'updated_192.jpg'); - strictEqual(updatedImageInBServer.isSensitive, false); - strictEqual(updatedImageInBServer.name, '192.jpg'); + strictEqual(updatedImageInB.isSensitive, false); + strictEqual(updatedImageInB.name, '192.jpg'); }); }); - let reupdatedImageInBServer: Misskey.entities.DriveFile; + let reupdatedImageInB: Misskey.entities.DriveFile; describe('Re-update with attaching to Note', () => { beforeAll(async () => { const noteWithUpdatedImage = (await uploader.client.request('notes/create', { fileIds: [updatedImage.id] })).createdNote; - const noteWithUpdatedImageInBServer = await resolveRemoteNote('a.test', noteWithUpdatedImage.id, bAdmin); - assert(noteWithUpdatedImageInBServer.files != null); - strictEqual(noteWithUpdatedImageInBServer.files.length, 1); - reupdatedImageInBServer = noteWithUpdatedImageInBServer.files[0]; + const noteWithUpdatedImageInB = await resolveRemoteNote('a.test', noteWithUpdatedImage.id, bAdmin); + assert(noteWithUpdatedImageInB.files != null); + strictEqual(noteWithUpdatedImageInB.files.length, 1); + reupdatedImageInB = noteWithUpdatedImageInB.files[0]; }); test('Check consistency', () => { - // console.log(`b.test: ${JSON.stringify(reupdatedImageInBServer, null, '\t')}`); + // console.log(`b.test: ${JSON.stringify(reupdatedImageInB, null, '\t')}`); // `isSensitive` is updated - strictEqual(reupdatedImageInBServer.isSensitive, true); + strictEqual(reupdatedImageInB.isSensitive, true); // FIXME: but `name` is not updated - strictEqual(reupdatedImageInBServer.name, '192.jpg'); + strictEqual(reupdatedImageInB.name, '192.jpg'); }); }); }); @@ -91,7 +91,7 @@ describe('Drive', () => { describe('Sensitive flag', () => { describe('isSensitive is federated in delivering to followers', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -99,12 +99,12 @@ describe('Drive', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); - await bob.client.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInB.id }); await sleep(); }); @@ -116,10 +116,10 @@ describe('Drive', () => { const notes = await bob.client.request('notes/timeline', {}); strictEqual(notes.length, 1); - const noteInBServer = notes[0]; - assert(noteInBServer.files != null); - strictEqual(noteInBServer.files.length, 1); - strictEqual(noteInBServer.files[0].isSensitive, true); + const noteInB = notes[0]; + assert(noteInB.files != null); + strictEqual(noteInB.files.length, 1); + strictEqual(noteInB.files[0].isSensitive, true); }); }); @@ -138,10 +138,10 @@ describe('Drive', () => { await alice.client.request('drive/files/update', { fileId: file.id, isSensitive: true }); const note = (await alice.client.request('notes/create', { text: 'sensitive', fileIds: [file.id] })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - assert(noteInBServer.files != null); - strictEqual(noteInBServer.files.length, 1); - strictEqual(noteInBServer.files[0].isSensitive, true); + const noteInB = await resolveRemoteNote('a.test', note.id, bob); + assert(noteInB.files != null); + strictEqual(noteInB.files.length, 1); + strictEqual(noteInB.files[0].isSensitive, true); }); }); @@ -161,14 +161,14 @@ describe('Drive', () => { const file = await uploadFile('a.test', '../../test/resources/192.jpg', alice.i); await alice.client.request('drive/files/update', { fileId: file.id, isSensitive: true }); - const bobNoteInAServer = await resolveRemoteNote('b.test', bobNote.id, alice); - const note = (await alice.client.request('notes/create', { text: 'sensitive', fileIds: [file.id], replyId: bobNoteInAServer.id })).createdNote; + const bobNoteInA = await resolveRemoteNote('b.test', bobNote.id, alice); + const note = (await alice.client.request('notes/create', { text: 'sensitive', fileIds: [file.id], replyId: bobNoteInA.id })).createdNote; await sleep(); - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); - assert(noteInBServer.files != null); - strictEqual(noteInBServer.files.length, 1); - strictEqual(noteInBServer.files[0].isSensitive, true); + const noteInB = await resolveRemoteNote('a.test', note.id, bob); + assert(noteInB.files != null); + strictEqual(noteInB.files.length, 1); + strictEqual(noteInB.files[0].isSensitive, true); }); }); }); diff --git a/packages/backend/test-federation/test/emoji.test.ts b/packages/backend/test-federation/test/emoji.test.ts index 35ee226168b3..3119ca6e4de5 100644 --- a/packages/backend/test-federation/test/emoji.test.ts +++ b/packages/backend/test-federation/test/emoji.test.ts @@ -4,7 +4,7 @@ import { addCustomEmoji, createAccount, type LoginUser, resolveRemoteUser, sleep describe('Emoji', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -12,12 +12,12 @@ describe('Emoji', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); - await bob.client.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInB.id }); await sleep(); }); @@ -27,12 +27,12 @@ describe('Emoji', () => { await sleep(); const notes = await bob.client.request('notes/timeline', {}); - const noteInBServer = notes[0]; + const noteInB = notes[0]; - strictEqual(noteInBServer.text, `I love \u200b:${emoji.name}:\u200b`); - assert(noteInBServer.emojis != null); - assert(emoji.name in noteInBServer.emojis); - strictEqual(noteInBServer.emojis[emoji.name], emoji.url); + strictEqual(noteInB.text, `I love \u200b:${emoji.name}:\u200b`); + assert(noteInB.emojis != null); + assert(emoji.name in noteInB.emojis); + strictEqual(noteInB.emojis[emoji.name], emoji.url); }); test('Custom emoji are delivered with Reaction delivery', async () => { @@ -43,9 +43,9 @@ describe('Emoji', () => { await alice.client.request('notes/reactions/create', { noteId: note.id, reaction: `:${emoji.name}:` }); await sleep(); - const noteInBServer = (await bob.client.request('notes/timeline', {}))[0]; - deepStrictEqual(noteInBServer.reactions[`:${emoji.name}@a.test:`], 1); - deepStrictEqual(noteInBServer.reactionEmojis[`${emoji.name}@a.test`], emoji.url); + const noteInB = (await bob.client.request('notes/timeline', {}))[0]; + deepStrictEqual(noteInB.reactions[`:${emoji.name}@a.test:`], 1); + deepStrictEqual(noteInB.reactionEmojis[`${emoji.name}@a.test`], emoji.url); }); test('Custom emoji are delivered with Profile delivery', async () => { @@ -53,10 +53,10 @@ describe('Emoji', () => { const renewedAlice = await alice.client.request('i/update', { name: `:${emoji.name}:` }); await sleep(); - const renewedAliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); - strictEqual(renewedAliceInBServer.name, renewedAlice.name); - assert(emoji.name in renewedAliceInBServer.emojis); - strictEqual(renewedAliceInBServer.emojis[emoji.name], emoji.url); + const renewedaliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); + strictEqual(renewedaliceInB.name, renewedAlice.name); + assert(emoji.name in renewedaliceInB.emojis); + strictEqual(renewedaliceInB.emojis[emoji.name], emoji.url); }); test('Local-only custom emoji aren\'t delivered with Note delivery', async () => { @@ -65,11 +65,11 @@ describe('Emoji', () => { await sleep(); const notes = await bob.client.request('notes/timeline', {}); - const noteInBServer = notes[0]; + const noteInB = notes[0]; - strictEqual(noteInBServer.text, `I love \u200b:${emoji.name}:\u200b`); - // deepStrictEqual(noteInBServer.emojis, {}); // TODO: this fails (why?) - deepStrictEqual({ ...noteInBServer.emojis }, {}); + strictEqual(noteInB.text, `I love \u200b:${emoji.name}:\u200b`); + // deepStrictEqual(noteInB.emojis, {}); // TODO: this fails (why?) + deepStrictEqual({ ...noteInB.emojis }, {}); }); test('Local-only custom emoji aren\'t delivered with Reaction delivery', async () => { @@ -80,9 +80,9 @@ describe('Emoji', () => { await alice.client.request('notes/reactions/create', { noteId: note.id, reaction: `:${emoji.name}:` }); await sleep(); - const noteInBServer = (await bob.client.request('notes/timeline', {}))[0]; - deepStrictEqual({ ...noteInBServer.reactions }, { '❤': 1 }); - deepStrictEqual({ ...noteInBServer.reactionEmojis }, {}); + const noteInB = (await bob.client.request('notes/timeline', {}))[0]; + deepStrictEqual({ ...noteInB.reactions }, { '❤': 1 }); + deepStrictEqual({ ...noteInB.reactionEmojis }, {}); }); test('Local-only custom emoji aren\'t delivered with Profile delivery', async () => { @@ -90,8 +90,8 @@ describe('Emoji', () => { const renewedAlice = await alice.client.request('i/update', { name: `:${emoji.name}:` }); await sleep(); - const renewedAliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); - strictEqual(renewedAliceInBServer.name, renewedAlice.name); - deepStrictEqual({ ...renewedAliceInBServer.emojis }, {}); + const renewedaliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); + strictEqual(renewedaliceInB.name, renewedAlice.name); + deepStrictEqual({ ...renewedaliceInB.emojis }, {}); }); }); diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index 88103c34634a..69aadd375a83 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -4,7 +4,7 @@ import { addCustomEmoji, createAccount, deepStrictEqualWithExcludedFields, type describe('Note', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -12,7 +12,7 @@ describe('Note', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); @@ -44,7 +44,7 @@ describe('Note', () => { 'user', 'uri', ]); - strictEqual(aliceInBServer.id, resolvedNote.userId); + strictEqual(aliceInB.id, resolvedNote.userId); }); test('Consistency of reply', async () => { @@ -83,7 +83,7 @@ describe('Note', () => { // flaky because this is parallelly incremented, so let's check it below 'repliesCount', ]); - strictEqual(aliceInBServer.id, resolvedNote.userId); + strictEqual(aliceInB.id, resolvedNote.userId); await sleep(); @@ -121,7 +121,7 @@ describe('Note', () => { 'user', 'uri', ]); - strictEqual(aliceInBServer.id, resolvedNote.userId); + strictEqual(aliceInB.id, resolvedNote.userId); }); }); @@ -148,18 +148,18 @@ describe('Note', () => { beforeAll(async () => { carol = await createAccount('a.test'); - await carol.client.request('following/create', { userId: bobInAServer.id }); + await carol.client.request('following/create', { userId: bobInA.id }); await sleep(); }); test('Delete is derivered to followers', async () => { const note = (await bob.client.request('notes/create', { text: 'I\'m Bob.' })).createdNote; - const noteInAServer = await resolveRemoteNote('b.test', note.id, carol); + const noteInA = await resolveRemoteNote('b.test', note.id, carol); await bob.client.request('notes/delete', { noteId: note.id }); await sleep(); await rejects( - async () => await carol.client.request('notes/show', { noteId: noteInAServer.id }), + async () => await carol.client.request('notes/show', { noteId: noteInA.id }), (err: any) => { strictEqual(err.code, 'NO_SUCH_NOTE'); return true; @@ -180,7 +180,7 @@ describe('Note', () => { const reactions = await alice.client.request('notes/reactions', { noteId: note.id }); strictEqual(reactions.length, 1); strictEqual(reactions[0].type, reaction); - strictEqual(reactions[0].user.id, bobInAServer.id); + strictEqual(reactions[0].user.id, bobInA.id); }); test('Custom emoji reaction', async () => { @@ -193,16 +193,16 @@ describe('Note', () => { const reactions = await alice.client.request('notes/reactions', { noteId: note.id }); strictEqual(reactions.length, 1); strictEqual(reactions[0].type, `:${emoji.name}@b.test:`); - strictEqual(reactions[0].user.id, bobInAServer.id); + strictEqual(reactions[0].user.id, bobInA.id); }); }); describe('Acceptance', () => { test('Even if likeOnly, remote users can react with custom emoji, but it is converted to like', async () => { const note = (await alice.client.request('notes/create', { text: 'a', reactionAcceptance: 'likeOnly' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const noteInB = await resolveRemoteNote('a.test', note.id, bob); const emoji = await addCustomEmoji('b.test'); - await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction: `:${emoji.name}:` }); + await bob.client.request('notes/reactions/create', { noteId: noteInB.id, reaction: `:${emoji.name}:` }); await sleep(); const reactions = await alice.client.request('notes/reactions', { noteId: note.id }); @@ -216,9 +216,9 @@ describe('Note', () => { */ test('Even if nonSensitiveOnly, remote users can react with sensitive emoji, and it is not converted', async () => { const note = (await alice.client.request('notes/create', { text: 'a', reactionAcceptance: 'nonSensitiveOnly' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const noteInB = await resolveRemoteNote('a.test', note.id, bob); const emoji = await addCustomEmoji('b.test', { isSensitive: true }); - await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction: `:${emoji.name}:` }); + await bob.client.request('notes/reactions/create', { noteId: noteInB.id, reaction: `:${emoji.name}:` }); await sleep(); const reactions = await alice.client.request('notes/reactions', { noteId: note.id }); @@ -238,8 +238,8 @@ describe('Note', () => { test('Bob creates poll and receives a vote from Carol', async () => { const note = (await bob.client.request('notes/create', { poll: { choices: ['inu', 'neko'] } })).createdNote; - const noteInAServer = await resolveRemoteNote('b.test', note.id, carol); - await carol.client.request('notes/polls/vote', { noteId: noteInAServer.id, choice: 0 }); + const noteInA = await resolveRemoteNote('b.test', note.id, carol); + await carol.client.request('notes/polls/vote', { noteId: noteInA.id, choice: 0 }); await sleep(); const noteAfterVote = await bob.client.request('notes/show', { noteId: note.id }); @@ -261,18 +261,18 @@ describe('Note', () => { createAccount('b.test'), ]); - await bobRemoteFollower.client.request('following/create', { userId: bobInAServer.id }); + await bobRemoteFollower.client.request('following/create', { userId: bobInA.id }); await sleep(); }); test('A vote in Bob\'s server is delivered to Bob\'s remote followers', async () => { const note = (await bob.client.request('notes/create', { poll: { choices: ['inu', 'neko'] } })).createdNote; // NOTE: resolve before voting - const noteInAServer = await resolveRemoteNote('b.test', note.id, bobRemoteFollower); + const noteInA = await resolveRemoteNote('b.test', note.id, bobRemoteFollower); await localVoter.client.request('notes/polls/vote', { noteId: note.id, choice: 0 }); await sleep(); - const noteAfterVote = await bobRemoteFollower.client.request('notes/show', { noteId: noteInAServer.id }); + const noteAfterVote = await bobRemoteFollower.client.request('notes/show', { noteId: noteInA.id }); assert(noteAfterVote.poll != null); strictEqual(noteAfterVote.poll.choices[0].votes, 1); strictEqual(noteAfterVote.poll.choices[1].votes, 0); diff --git a/packages/backend/test-federation/test/notification.test.ts b/packages/backend/test-federation/test/notification.test.ts index 52f43f4e67b0..6d55353653e6 100644 --- a/packages/backend/test-federation/test/notification.test.ts +++ b/packages/backend/test-federation/test/notification.test.ts @@ -3,7 +3,7 @@ import { assertNotificationReceived, createAccount, type LoginUser, resolveRemot describe('Notification', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -11,7 +11,7 @@ describe('Notification', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); @@ -21,75 +21,75 @@ describe('Notification', () => { test('Get notification when follow', async () => { await assertNotificationReceived( 'b.test', bob, - async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), - notification => notification.type === 'followRequestAccepted' && notification.userId === aliceInBServer.id, + async () => await bob.client.request('following/create', { userId: aliceInB.id }), + notification => notification.type === 'followRequestAccepted' && notification.userId === aliceInB.id, true, ); - await bob.client.request('following/delete', { userId: aliceInBServer.id }); + await bob.client.request('following/delete', { userId: aliceInB.id }); await sleep(); }); test('Get notification when get followed', async () => { await assertNotificationReceived( 'a.test', alice, - async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), - notification => notification.type === 'follow' && notification.userId === bobInAServer.id, + async () => await bob.client.request('following/create', { userId: aliceInB.id }), + notification => notification.type === 'follow' && notification.userId === bobInA.id, true, ); }); - afterAll(async () => await bob.client.request('following/delete', { userId: aliceInBServer.id })); + afterAll(async () => await bob.client.request('following/delete', { userId: aliceInB.id })); }); describe('Note', () => { test('Get notification when get a reaction', async () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const noteInB = await resolveRemoteNote('a.test', note.id, bob); const reaction = '😅'; await assertNotificationReceived( 'a.test', alice, - async () => await bob.client.request('notes/reactions/create', { noteId: noteInBServer.id, reaction }), + async () => await bob.client.request('notes/reactions/create', { noteId: noteInB.id, reaction }), notification => - notification.type === 'reaction' && notification.note.id === note.id && notification.userId === bobInAServer.id && notification.reaction === reaction, + notification.type === 'reaction' && notification.note.id === note.id && notification.userId === bobInA.id && notification.reaction === reaction, true, ); }); test('Get notification when replied', async () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const noteInB = await resolveRemoteNote('a.test', note.id, bob); const text = crypto.randomUUID(); await assertNotificationReceived( 'a.test', alice, - async () => await bob.client.request('notes/create', { text, replyId: noteInBServer.id }), + async () => await bob.client.request('notes/create', { text, replyId: noteInB.id }), notification => - notification.type === 'reply' && notification.note.reply!.id === note.id && notification.userId === bobInAServer.id && notification.note.text === text, + notification.type === 'reply' && notification.note.reply!.id === note.id && notification.userId === bobInA.id && notification.note.text === text, true, ); }); test('Get notification when renoted', async () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const noteInB = await resolveRemoteNote('a.test', note.id, bob); await assertNotificationReceived( 'a.test', alice, - async () => await bob.client.request('notes/create', { renoteId: noteInBServer.id }), + async () => await bob.client.request('notes/create', { renoteId: noteInB.id }), notification => - notification.type === 'renote' && notification.note.renote!.id === note.id && notification.userId === bobInAServer.id, + notification.type === 'renote' && notification.note.renote!.id === note.id && notification.userId === bobInA.id, true, ); }); test('Get notification when quoted', async () => { const note = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - const noteInBServer = await resolveRemoteNote('a.test', note.id, bob); + const noteInB = await resolveRemoteNote('a.test', note.id, bob); const text = crypto.randomUUID(); await assertNotificationReceived( 'a.test', alice, - async () => await bob.client.request('notes/create', { text, renoteId: noteInBServer.id }), + async () => await bob.client.request('notes/create', { text, renoteId: noteInB.id }), notification => - notification.type === 'quote' && notification.note.renote!.id === note.id && notification.userId === bobInAServer.id && notification.note.text === text, + notification.type === 'quote' && notification.note.renote!.id === note.id && notification.userId === bobInA.id && notification.note.text === text, true, ); }); @@ -99,7 +99,7 @@ describe('Notification', () => { await assertNotificationReceived( 'a.test', alice, async () => await bob.client.request('notes/create', { text }), - notification => notification.type === 'mention' && notification.userId === bobInAServer.id && notification.note.text === text, + notification => notification.type === 'mention' && notification.userId === bobInA.id && notification.note.text === text, true, ); }); diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index 9f14b733b332..c2864bf1173b 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -6,7 +6,7 @@ const bAdmin = await fetchAdmin('b.test'); describe('Timeline', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -14,12 +14,12 @@ describe('Timeline', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); - await bob.client.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInB.id }); await sleep(); }); @@ -64,16 +64,16 @@ describe('Timeline', () => { await sleep(); const notes = await (bob.client.request as Request)(endpoint, params); - const noteInBServer = notes.filter(({ uri }) => uri === `https://a.test/notes/${note!.id}`).pop(); - const endpointFired = noteInBServer != null; + const noteInB = notes.filter(({ uri }) => uri === `https://a.test/notes/${note!.id}`).pop(); + const endpointFired = noteInB != null; strictEqual(endpointFired, expect); // Let's check Delete reception if (expect) { const streamingFired = await isNoteUpdatedEventFired( - 'b.test', bob, noteInBServer!.id, + 'b.test', bob, noteInB!.id, async () => await alice.client.request('notes/delete', { noteId: note!.id }), - msg => msg.type === 'deleted' && msg.id === noteInBServer!.id, + msg => msg.type === 'deleted' && msg.id === noteInB!.id, ); strictEqual(streamingFired, true); @@ -102,7 +102,7 @@ describe('Timeline', () => { }); test('Receive remote followee\'s visible specified-only Note', async () => { - await postAndCheckReception(homeTimeline, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + await postAndCheckReception(homeTimeline, true, { visibility: 'specified', visibleUserIds: [bobInA.id] }); }); test('Don\'t receive remote followee\'s localOnly Note', async () => { @@ -127,7 +127,7 @@ describe('Timeline', () => { */ test.failing('Receive remote followee\'s visible specified-only reply to invisible specified-only Note', async () => { const note = (await alice.client.request('notes/create', { text: 'a', visibility: 'specified' })).createdNote; - await postAndCheckReception(homeTimeline, true, { replyId: note.id, visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + await postAndCheckReception(homeTimeline, true, { replyId: note.id, visibility: 'specified', visibleUserIds: [bobInA.id] }); }); }); }); @@ -159,7 +159,7 @@ describe('Timeline', () => { }); test('Receive remote followee\'s visible specified-only Note', async () => { - await postAndCheckReception(hybridTimeline, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + await postAndCheckReception(hybridTimeline, true, { visibility: 'specified', visibleUserIds: [bobInA.id] }); }); }); }); @@ -181,7 +181,7 @@ describe('Timeline', () => { }); test('Don\'t receive remote followee\'s visible specified-only Note', async () => { - await postAndCheckReception(globalTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }); + await postAndCheckReception(globalTimeline, false, { visibility: 'specified', visibleUserIds: [bobInA.id] }); }); }); }); @@ -193,7 +193,7 @@ describe('Timeline', () => { beforeAll(async () => { list = await bob.client.request('users/lists/create', { name: 'Bob\'s List' }); - await bob.client.request('users/lists/push', { listId: list.id, userId: aliceInBServer.id }); + await bob.client.request('users/lists/push', { listId: list.id, userId: aliceInB.id }); await sleep(); }); @@ -211,7 +211,7 @@ describe('Timeline', () => { }); test('Receive remote followee\'s visible specified-only Note', async () => { - await postAndCheckReception(userList, true, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { listId: list.id }); + await postAndCheckReception(userList, true, { visibility: 'specified', visibleUserIds: [bobInA.id] }, { listId: list.id }); }); }); }); @@ -237,7 +237,7 @@ describe('Timeline', () => { test('Receive remote followee\'s visible specified-only Note', async () => { const tag = crypto.randomUUID(); - await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { q: [[tag]] }); + await postAndCheckReception(hashtag, true, { text: `#${tag}`, visibility: 'specified', visibleUserIds: [bobInA.id] }, { q: [[tag]] }); }); }); }); @@ -284,7 +284,7 @@ describe('Timeline', () => { }); test('Don\'t receive remote followee\'s visible specified-only Note', async () => { - await postAndCheckReception(roleTimeline, false, { visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { roleId: role.id }); + await postAndCheckReception(roleTimeline, false, { visibility: 'specified', visibleUserIds: [bobInA.id] }, { roleId: role.id }); }); }); @@ -328,7 +328,7 @@ describe('Timeline', () => { }); test('Don\'t receive remote followee\'s visible specified-only Note', async () => { - await postAndCheckReception(antenna, false, { text: 'I love Bob (4)', visibility: 'specified', visibleUserIds: [bobInAServer.id] }, { antennaId: bobAntenna.id }); + await postAndCheckReception(antenna, false, { text: 'I love Bob (4)', visibility: 'specified', visibleUserIds: [bobInA.id] }, { antennaId: bobAntenna.id }); }); }); diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index db48f3b75cb6..9c2937c82fae 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -12,13 +12,13 @@ describe('User', () => { describe('Consistency of profile', () => { let alice: LoginUser; let aliceWatcher: LoginUser; - let aliceWatcherInBServer: LoginUser; + let aliceWatcherInB: LoginUser; beforeAll(async () => { alice = await createAccount('a.test'); [ aliceWatcher, - aliceWatcherInBServer, + aliceWatcherInB, ] = await Promise.all([ createAccount('a.test'), createAccount('b.test'), @@ -26,14 +26,14 @@ describe('User', () => { }); test('Check consistency', async () => { - const aliceInAServer = await aliceWatcher.client.request('users/show', { userId: alice.id }); - const resolved = await resolveRemoteUser('a.test', aliceInAServer.id, aliceWatcherInBServer); - const aliceInBServer = await aliceWatcherInBServer.client.request('users/show', { userId: resolved.id }); + const aliceInA = await aliceWatcher.client.request('users/show', { userId: alice.id }); + const resolved = await resolveRemoteUser('a.test', aliceInA.id, aliceWatcherInB); + const aliceInB = await aliceWatcherInB.client.request('users/show', { userId: resolved.id }); - // console.log(`a.test: ${JSON.stringify(aliceInAServer, null, '\t')}`); - // console.log(`b.test: ${JSON.stringify(aliceInBServer, null, '\t')}`); + // console.log(`a.test: ${JSON.stringify(aliceInA, null, '\t')}`); + // console.log(`b.test: ${JSON.stringify(aliceInB, null, '\t')}`); - deepStrictEqualWithExcludedFields(aliceInAServer, aliceInBServer, [ + deepStrictEqualWithExcludedFields(aliceInA, aliceInB, [ 'id', 'host', 'avatarUrl', @@ -50,7 +50,7 @@ describe('User', () => { describe('isCat is federated', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -58,40 +58,40 @@ describe('User', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); }); test('Not isCat for default', () => { - strictEqual(aliceInBServer.isCat, false); + strictEqual(aliceInB.isCat, false); }); test('Becoming a cat is sent to their followers', async () => { - await bob.client.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInB.id }); await sleep(); await alice.client.request('i/update', { isCat: true }); await sleep(); - const res = await bob.client.request('users/show', { userId: aliceInBServer.id }); + const res = await bob.client.request('users/show', { userId: aliceInB.id }); strictEqual(res.isCat, true); }); }); describe('Pinning Notes', () => { let alice: LoginUser, bob: LoginUser; - let aliceInBServer: Misskey.entities.UserDetailedNotMe; + let aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ createAccount('a.test'), createAccount('b.test'), ]); - aliceInBServer = await resolveRemoteUser('a.test', alice.id, bob); + aliceInB = await resolveRemoteUser('a.test', alice.id, bob); - await bob.client.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInB.id }); }); test('Pinning localOnly Note is not delivered', async () => { @@ -99,8 +99,8 @@ describe('User', () => { await alice.client.request('i/pin', { noteId: note.id }); await sleep(); - const _aliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); - strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); + const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); + strictEqual(_aliceInB.pinnedNoteIds.length, 0); }); test('Pinning followers-only Note is not delivered', async () => { @@ -108,8 +108,8 @@ describe('User', () => { await alice.client.request('i/pin', { noteId: note.id }); await sleep(); - const _aliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); - strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); + const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); + strictEqual(_aliceInB.pinnedNoteIds.length, 0); }); let pinnedNote: Misskey.entities.Note; @@ -119,25 +119,25 @@ describe('User', () => { await alice.client.request('i/pin', { noteId: pinnedNote.id }); await sleep(); - const _aliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); - strictEqual(_aliceInBServer.pinnedNoteIds.length, 1); - const pinnedNoteInBServer = await resolveRemoteNote('a.test', pinnedNote.id, bob); - strictEqual(_aliceInBServer.pinnedNotes[0].id, pinnedNoteInBServer.id); + const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); + strictEqual(_aliceInB.pinnedNoteIds.length, 1); + const pinnedNoteInB = await resolveRemoteNote('a.test', pinnedNote.id, bob); + strictEqual(_aliceInB.pinnedNotes[0].id, pinnedNoteInB.id); }); test('Unpinning normal Note is delivered', async () => { await alice.client.request('i/unpin', { noteId: pinnedNote.id }); await sleep(); - const _aliceInBServer = await bob.client.request('users/show', { userId: aliceInBServer.id }); - strictEqual(_aliceInBServer.pinnedNoteIds.length, 0); + const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); + strictEqual(_aliceInB.pinnedNoteIds.length, 0); }); }); }); describe('Follow / Unfollow', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -145,7 +145,7 @@ describe('User', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); @@ -153,7 +153,7 @@ describe('User', () => { describe('Follow a.test ==> b.test', () => { beforeAll(async () => { - await alice.client.request('following/create', { userId: bobInAServer.id }); + await alice.client.request('following/create', { userId: bobInA.id }); await sleep(); }); @@ -162,12 +162,12 @@ describe('User', () => { await Promise.all([ strictEqual( (await alice.client.request('users/following', { userId: alice.id })) - .some(v => v.followeeId === bobInAServer.id), + .some(v => v.followeeId === bobInA.id), true, ), strictEqual( (await bob.client.request('users/followers', { userId: bob.id })) - .some(v => v.followerId === aliceInBServer.id), + .some(v => v.followerId === aliceInB.id), true, ), ]); @@ -176,7 +176,7 @@ describe('User', () => { describe('Unfollow a.test ==> b.test', () => { beforeAll(async () => { - await alice.client.request('following/delete', { userId: bobInAServer.id }); + await alice.client.request('following/delete', { userId: bobInA.id }); await sleep(); }); @@ -185,12 +185,12 @@ describe('User', () => { await Promise.all([ strictEqual( (await alice.client.request('users/following', { userId: alice.id })) - .some(v => v.followeeId === bobInAServer.id), + .some(v => v.followeeId === bobInA.id), false, ), strictEqual( (await bob.client.request('users/followers', { userId: bob.id })) - .some(v => v.followerId === aliceInBServer.id), + .some(v => v.followerId === aliceInB.id), false, ), ]); @@ -200,7 +200,7 @@ describe('User', () => { describe('Follow requests', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -208,7 +208,7 @@ describe('User', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); @@ -219,7 +219,7 @@ describe('User', () => { describe('Send follow request from Bob to Alice and cancel', () => { describe('Bob sends follow request to Alice', () => { beforeAll(async () => { - await bob.client.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInB.id }); await sleep(); }); @@ -227,13 +227,13 @@ describe('User', () => { const requests = await alice.client.request('following/requests/list', {}); strictEqual(requests.length, 1); strictEqual(requests[0].followee.id, alice.id); - strictEqual(requests[0].follower.id, bobInAServer.id); + strictEqual(requests[0].follower.id, bobInA.id); }); }); describe('Alice cancels it', () => { beforeAll(async () => { - await bob.client.request('following/requests/cancel', { userId: aliceInBServer.id }); + await bob.client.request('following/requests/cancel', { userId: aliceInB.id }); await sleep(); }); @@ -246,16 +246,16 @@ describe('User', () => { describe('Send follow request from Bob to Alice and reject', () => { beforeAll(async () => { - await bob.client.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInB.id }); await sleep(); - await alice.client.request('following/requests/reject', { userId: bobInAServer.id }); + await alice.client.request('following/requests/reject', { userId: bobInA.id }); await sleep(); }); test('Bob should have no requests', async () => { await rejects( - async () => await bob.client.request('following/requests/cancel', { userId: aliceInBServer.id }), + async () => await bob.client.request('following/requests/cancel', { userId: aliceInB.id }), (err: any) => { strictEqual(err.code, 'FOLLOW_REQUEST_NOT_FOUND'); return true; @@ -271,17 +271,17 @@ describe('User', () => { describe('Send follow request from Bob to Alice and accept', () => { beforeAll(async () => { - await bob.client.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInB.id }); await sleep(); - await alice.client.request('following/requests/accept', { userId: bobInAServer.id }); + await alice.client.request('following/requests/accept', { userId: bobInA.id }); await sleep(); }); test('Bob follows Alice', async () => { const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 1); - strictEqual(following[0].followeeId, aliceInBServer.id); + strictEqual(following[0].followeeId, aliceInB.id); strictEqual(following[0].followerId, bob.id); }); }); @@ -290,7 +290,7 @@ describe('User', () => { describe('Deletion', () => { describe('Check Delete consistency', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -298,14 +298,14 @@ describe('User', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); }); test('Bob follows Alice, and Alice deleted themself', async () => { - await bob.client.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInB.id }); await sleep(); const followers = await alice.client.request('users/followers', { userId: alice.id }); @@ -318,7 +318,7 @@ describe('User', () => { strictEqual(following.length, 0); // no following relation await rejects( - async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + async () => await bob.client.request('following/create', { userId: aliceInB.id }), (err: any) => { strictEqual(err.code, 'NO_SUCH_USER'); return true; @@ -329,7 +329,7 @@ describe('User', () => { describe('Deletion of remote user for moderation', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -337,27 +337,27 @@ describe('User', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); }); test('Bob follows Alice, then Alice gets deleted in B server', async () => { - await bob.client.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInB.id }); await sleep(); const followers = await alice.client.request('users/followers', { userId: alice.id }); strictEqual(followers.length, 1); // followed by Bob - await bAdmin.client.request('admin/delete-account', { userId: aliceInBServer.id }); + await bAdmin.client.request('admin/delete-account', { userId: aliceInB.id }); await sleep(); // TODO: why still following relation? const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 1); await rejects( - async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + async () => await bob.client.request('following/create', { userId: aliceInB.id }), (err: any) => { strictEqual(err.code, 'ALREADY_FOLLOWING'); return true; @@ -366,7 +366,7 @@ describe('User', () => { }); test('Alice tries to follow Bob, but it is not processed', async () => { - await alice.client.request('following/create', { userId: bobInAServer.id }); + await alice.client.request('following/create', { userId: bobInA.id }); await sleep(); const following = await alice.client.request('users/following', { userId: alice.id }); @@ -381,7 +381,7 @@ describe('User', () => { describe('Suspension', () => { describe('Check suspend/unsuspend consistency', () => { let alice: LoginUser, bob: LoginUser; - let bobInAServer: Misskey.entities.UserDetailedNotMe, aliceInBServer: Misskey.entities.UserDetailedNotMe; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; beforeAll(async () => { [alice, bob] = await Promise.all([ @@ -389,14 +389,14 @@ describe('User', () => { createAccount('b.test'), ]); - [bobInAServer, aliceInBServer] = await Promise.all([ + [bobInA, aliceInB] = await Promise.all([ resolveRemoteUser('b.test', bob.id, alice), resolveRemoteUser('a.test', alice.id, bob), ]); }); test('Bob follows Alice, and Alice gets suspended, there is no following relation, and Bob fails to follow again', async () => { - await bob.client.request('following/create', { userId: aliceInBServer.id }); + await bob.client.request('following/create', { userId: aliceInB.id }); await sleep(); const followers = await alice.client.request('users/followers', { userId: alice.id }); @@ -409,7 +409,7 @@ describe('User', () => { strictEqual(following.length, 0); // no following relation await rejects( - async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + async () => await bob.client.request('following/create', { userId: aliceInB.id }), (err: any) => { strictEqual(err.code, 'NO_SUCH_USER'); return true; @@ -430,7 +430,7 @@ describe('User', () => { * related @see https://github.com/misskey-dev/misskey/issues/13273 */ await rejects( - async () => await bob.client.request('following/create', { userId: aliceInBServer.id }), + async () => await bob.client.request('following/create', { userId: aliceInB.id }), (err: any) => { strictEqual(err.code, 'NO_SUCH_USER'); return true; @@ -451,22 +451,22 @@ describe('User', () => { * instead of simple unsuspension, let's tell existence by following from Alice */ test('Alice can follow Bob', async () => { - await alice.client.request('following/create', { userId: bobInAServer.id }); + await alice.client.request('following/create', { userId: bobInA.id }); await sleep(); const bobFollowers = await bob.client.request('users/followers', { userId: bob.id }); strictEqual(bobFollowers.length, 1); // followed by Alice assert(bobFollowers[0].follower != null); - const renewedAliceInBServer = bobFollowers[0].follower; - assert(aliceInBServer.username === renewedAliceInBServer.username); - assert(aliceInBServer.host === renewedAliceInBServer.host); - assert(aliceInBServer.id !== renewedAliceInBServer.id); // TODO: Same username and host, but their ids are different! Is it OK? + const renewedaliceInB = bobFollowers[0].follower; + assert(aliceInB.username === renewedaliceInB.username); + assert(aliceInB.host === renewedaliceInB.host); + assert(aliceInB.id !== renewedaliceInB.id); // TODO: Same username and host, but their ids are different! Is it OK? const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 0); // following are deleted // Bob tries to follow Alice - await bob.client.request('following/create', { userId: renewedAliceInBServer.id }); + await bob.client.request('following/create', { userId: renewedaliceInB.id }); await sleep(); const aliceFollowers = await alice.client.request('users/followers', { userId: alice.id }); From 18095f863bd60c7054537b65038ed7f917ce3a8f Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:24:05 +0900 Subject: [PATCH 097/111] refactor(block): use existing function --- .../backend/test-federation/test/block.test.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/backend/test-federation/test/block.test.ts b/packages/backend/test-federation/test/block.test.ts index 1aea48ed246c..ef910eeaead7 100644 --- a/packages/backend/test-federation/test/block.test.ts +++ b/packages/backend/test-federation/test/block.test.ts @@ -1,6 +1,6 @@ import { deepStrictEqual, rejects, strictEqual } from 'node:assert'; import * as Misskey from 'misskey-js'; -import { createAccount, isFired, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; +import { assertNotificationReceived, createAccount, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; describe('Block', () => { describe('Check follow', () => { @@ -213,15 +213,12 @@ describe('Block', () => { await sleep(); const text = `@${alice.username}@a.test plz unblock me!`; - const fired = await isFired( - 'a.test', alice, 'main', - async () => { - await sleep(); - await bob.client.request('notes/create', { text }); - }, - 'notification', msg => msg.type === 'mention' && msg.userId === bobInA.id && msg.note.text === text, + await assertNotificationReceived( + 'a.test', alice, + async () => await bob.client.request('notes/create', { text }), + notification => notification.type === 'mention' && notification.userId === bobInA.id && notification.note.text === text, + true, ); - deepStrictEqual(fired, true); }); }); }); From 43a200099b09ee816f85184c7eb9328d38039b09 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:40:02 +0900 Subject: [PATCH 098/111] refactor: file upload --- packages/backend/test-federation/test/drive.test.ts | 8 ++++---- packages/backend/test-federation/test/note.test.ts | 2 +- packages/backend/test-federation/test/utils.ts | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/backend/test-federation/test/drive.test.ts b/packages/backend/test-federation/test/drive.test.ts index 0100f292527e..f755183b4dbe 100644 --- a/packages/backend/test-federation/test/drive.test.ts +++ b/packages/backend/test-federation/test/drive.test.ts @@ -16,7 +16,7 @@ describe('Drive', () => { describe('Upload', () => { beforeAll(async () => { - image = await uploadFile('a.test', '../../test/resources/192.jpg', uploader.i); + image = await uploadFile('a.test', uploader); const noteWithImage = (await uploader.client.request('notes/create', { fileIds: [image.id] })).createdNote; const noteInB = await resolveRemoteNote('a.test', noteWithImage.id, bAdmin); assert(noteInB.files != null); @@ -109,7 +109,7 @@ describe('Drive', () => { }); test('Alice uploads sensitive image and it is shown as sensitive from Bob', async () => { - const file = await uploadFile('a.test', '../../test/resources/192.jpg', alice.i); + const file = await uploadFile('a.test', alice); await alice.client.request('drive/files/update', { fileId: file.id, isSensitive: true }); await alice.client.request('notes/create', { text: 'sensitive', fileIds: [file.id] }); await sleep(); @@ -134,7 +134,7 @@ describe('Drive', () => { }); test('Alice uploads sensitive image and it is shown as sensitive from Bob', async () => { - const file = await uploadFile('a.test', '../../test/resources/192.jpg', alice.i); + const file = await uploadFile('a.test', alice); await alice.client.request('drive/files/update', { fileId: file.id, isSensitive: true }); const note = (await alice.client.request('notes/create', { text: 'sensitive', fileIds: [file.id] })).createdNote; @@ -159,7 +159,7 @@ describe('Drive', () => { test('Alice uploads sensitive image and it is shown as sensitive from Bob', async () => { const bobNote = (await bob.client.request('notes/create', { text: 'I\'m Bob' })).createdNote; - const file = await uploadFile('a.test', '../../test/resources/192.jpg', alice.i); + const file = await uploadFile('a.test', alice); await alice.client.request('drive/files/update', { fileId: file.id, isSensitive: true }); const bobNoteInA = await resolveRemoteNote('b.test', bobNote.id, alice); const note = (await alice.client.request('notes/create', { text: 'sensitive', fileIds: [file.id], replyId: bobNoteInA.id })).createdNote; diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts index 69aadd375a83..566585a8c03e 100644 --- a/packages/backend/test-federation/test/note.test.ts +++ b/packages/backend/test-federation/test/note.test.ts @@ -20,7 +20,7 @@ describe('Note', () => { describe('Note content', () => { test('Consistency of Public Note', async () => { - const image = await uploadFile('a.test', '../../test/resources/192.jpg', alice.i); + const image = await uploadFile('a.test', alice); const note = (await alice.client.request('notes/create', { text: 'I am Alice!', fileIds: [image.id], diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index c5ec40340cdb..07e9154025e9 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -148,12 +148,12 @@ export async function resolveRemoteNote(host: Host, id: string, from: LoginUser) }); } -export async function uploadFile(host: Host, path: string, token: string): Promise { +export async function uploadFile(host: Host, user: { i: string }, path = '../../test/resources/192.jpg'): Promise { const filename = path.split('/').pop() ?? 'untitled'; const blob = new Blob([await readFile(join(__dirname, path))]); const body = new FormData(); - body.append('i', token); + body.append('i', user.i); body.append('force', 'true'); body.append('file', blob); body.append('name', filename); @@ -170,10 +170,10 @@ export async function uploadFile(host: Host, path: string, token: string): Promi }); } -export async function addCustomEmoji(host: Host, param?: Partial, path = '../../test/resources/192.jpg'): Promise { +export async function addCustomEmoji(host: Host, param?: Partial, path?: string): Promise { const admin = await fetchAdmin(host); const name = crypto.randomUUID().replaceAll('-', ''); - const file = await uploadFile(host, path, admin.i); + const file = await uploadFile(host, admin, path); return await admin.client.request('admin/emoji/add', { name, fileId: file.id, ...param }); } From 1a5113975156ed50017ea701062b09b971cb442f Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 3 Oct 2024 18:46:39 +0900 Subject: [PATCH 099/111] docs: update description --- packages/backend/test-federation/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/backend/test-federation/README.md b/packages/backend/test-federation/README.md index 733b8b0c2d25..480cd1b726af 100644 --- a/packages/backend/test-federation/README.md +++ b/packages/backend/test-federation/README.md @@ -1,6 +1,12 @@ ## test-federation Test federation between two Misskey servers: `a.test` and `b.test`. +Before testing, you need to build the entire project, and change working directory to here: +```sh +pnpm build +cd packages/backend/test-federation +``` + First, you need to start servers by executing following commands: ```sh cp ./.config/example.docker.env ./.config/docker.env From 93eee7f2c9aec549a034bd870fe087def1d12ac6 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Thu, 3 Oct 2024 19:04:33 +0900 Subject: [PATCH 100/111] test(user): ffVisibility --- .../backend/test-federation/test/user.test.ts | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index 9c2937c82fae..f660d6d89f0e 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -48,6 +48,73 @@ describe('User', () => { }); }); + describe('ffVisibility is federated', () => { + let alice: LoginUser, bob: LoginUser; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [bobInA, aliceInB] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), + ]); + + // NOTE: follow each other + await Promise.all([ + alice.client.request('following/create', { userId: bobInA.id }), + bob.client.request('following/create', { userId: aliceInB.id }), + ]); + await sleep(); + }); + + test('Visibility set public by default', async () => { + for (const user of await Promise.all([ + alice.client.request('users/show', { userId: bobInA.id }), + bob.client.request('users/show', { userId: aliceInB.id }), + ])) { + strictEqual(user.followersVisibility, 'public'); + strictEqual(user.followingVisibility, 'public'); + } + }); + + /** FIXME: not working */ + test.skip('Setting private for followersVisibility is federated', async () => { + await Promise.all([ + alice.client.request('i/update', { followersVisibility: 'private' }), + bob.client.request('i/update', { followersVisibility: 'private' }), + ]); + await sleep(); + + for (const user of await Promise.all([ + alice.client.request('users/show', { userId: bobInA.id }), + bob.client.request('users/show', { userId: aliceInB.id }), + ])) { + strictEqual(user.followersVisibility, 'private'); + strictEqual(user.followingVisibility, 'public'); + } + }); + + test.skip('Setting private for followingVisibility is federated', async () => { + await Promise.all([ + alice.client.request('i/update', { followingVisibility: 'private' }), + bob.client.request('i/update', { followingVisibility: 'private' }), + ]); + await sleep(); + + for (const user of await Promise.all([ + alice.client.request('users/show', { userId: bobInA.id }), + bob.client.request('users/show', { userId: aliceInB.id }), + ])) { + strictEqual(user.followersVisibility, 'private'); + strictEqual(user.followingVisibility, 'private'); + } + }); + }); + describe('isCat is federated', () => { let alice: LoginUser, bob: LoginUser; let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; From f38b23e159b693ff8ffbb8793dd8f05fa4528b62 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sat, 5 Oct 2024 18:17:30 +0900 Subject: [PATCH 101/111] fix: `/api/signin` -> `/api/signin-flow` --- packages/backend/test-federation/test/utils.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 07e9154025e9..db20d407407e 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -10,7 +10,7 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); export const ADMIN_PARAMS = { username: 'admin', password: 'admin' }; -const adminCache = new Map(); +const adminCache = new Map(); let fetched = false; if (!fetched) { @@ -21,7 +21,9 @@ if (!fetched) { fetched = true; } -export type LoginUser = Misskey.entities.SigninResponse & { +type SigninResponse = Omit; + +export type LoginUser = SigninResponse & { client: Misskey.api.APIClient; username: string; password: string; @@ -38,11 +40,11 @@ export async function sleep(ms = 200): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } -async function signin(host: Host, params: Misskey.entities.SigninRequest): Promise { +async function signin(host: Host, params: Misskey.entities.SigninFlowRequest): Promise { // wait for a second to prevent hit rate limit await sleep(1000); // console.log(`Sign in to @${params.username}@${host} ...`); - return await (new Misskey.api.APIClient({ origin: `https://${host}` }).request as Request)('signin', params) + return await (new Misskey.api.APIClient({ origin: `https://${host}` }).request as Request)('signin-flow', params) .then(res => { // console.log(`Signed in to @${params.username}@${host}`); return res; @@ -53,7 +55,7 @@ async function signin(host: Host, params: Misskey.entities.SigninRequest): Promi return await signin(host, params); } throw err; - }); + }) as Misskey.entities.SigninFlowResponse & { finished: true }; } async function createAdmin(host: Host): Promise { From 7085fd7a064622bcab5d805d0e78e7bd984271d9 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sat, 5 Oct 2024 19:09:32 +0900 Subject: [PATCH 102/111] test: abuse report --- .../test-federation/test/abuse-report.test.ts | 52 +++++++++++++++++++ .../test-federation/test/timeline.test.ts | 15 +----- .../backend/test-federation/test/utils.ts | 32 ++++++++++++ 3 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 packages/backend/test-federation/test/abuse-report.test.ts diff --git a/packages/backend/test-federation/test/abuse-report.test.ts b/packages/backend/test-federation/test/abuse-report.test.ts new file mode 100644 index 000000000000..b54d6222b4af --- /dev/null +++ b/packages/backend/test-federation/test/abuse-report.test.ts @@ -0,0 +1,52 @@ +import { rejects, strictEqual } from 'node:assert'; +import * as Misskey from 'misskey-js'; +import { createAccount, createModerator, resolveRemoteUser, sleep, type LoginUser } from './utils.js'; + +describe('Abuse report', () => { + describe('Forwarding report', () => { + let alice: LoginUser, bob: LoginUser, aModerator: LoginUser, bModerator: LoginUser; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [aModerator, bModerator] = await Promise.all([ + createModerator('a.test'), + createModerator('b.test'), + ]); + + [bobInA, aliceInB] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), + ]); + }); + + test('Alice reports Bob, moderator in A forwards it, and B moderator receives it', async () => { + const comment = crypto.randomUUID(); + await alice.client.request('users/report-abuse', { userId: bobInA.id, comment }); + const reports = await aModerator.client.request('admin/abuse-user-reports', {}); + const report = reports.filter(report => report.comment === comment)[0]; + await aModerator.client.request('admin/forward-abuse-user-report', { reportId: report.id }); + await sleep(); + + const reportsInB = await bModerator.client.request('admin/abuse-user-reports', {}); + const reportInB = reportsInB.filter(report => report.comment.includes(comment))[0]; + // NOTE: reporter is not Alice, and is not moderator in A + strictEqual(reportInB.reporter.url, 'https://a.test/@instance.actor'); + strictEqual(reportInB.targetUserId, bob.id); + + // NOTE: cannot forward multiple times + await rejects( + async () => await aModerator.client.request('admin/forward-abuse-user-report', { reportId: report.id }), + (err: any) => { + strictEqual(err.code, 'INTERNAL_ERROR'); + strictEqual(err.info.e.message, 'The report has already been forwarded.'); + return true; + }, + ); + }); + }); +}); diff --git a/packages/backend/test-federation/test/timeline.test.ts b/packages/backend/test-federation/test/timeline.test.ts index c2864bf1173b..2250bf4a426d 100644 --- a/packages/backend/test-federation/test/timeline.test.ts +++ b/packages/backend/test-federation/test/timeline.test.ts @@ -1,6 +1,6 @@ import { strictEqual } from 'assert'; import * as Misskey from 'misskey-js'; -import { createAccount, fetchAdmin, isNoteUpdatedEventFired, isFired, type LoginUser, type Request, resolveRemoteUser, sleep } from './utils.js'; +import { createAccount, fetchAdmin, isNoteUpdatedEventFired, isFired, type LoginUser, type Request, resolveRemoteUser, sleep, createRole } from './utils.js'; const bAdmin = await fetchAdmin('b.test'); @@ -248,24 +248,13 @@ describe('Timeline', () => { let role: Misskey.entities.Role; beforeAll(async () => { - role = await bAdmin.client.request('admin/roles/create', { + role = await createRole('b.test', { name: 'Remote Users', description: 'Remote users are assigned to this role.', - color: null, - iconUrl: null, - target: 'conditional', condFormula: { /** TODO: @see https://github.com/misskey-dev/misskey/issues/14169 */ type: 'isRemote' as never, }, - isPublic: true, - isModerator: false, - isAdministrator: false, - isExplorable: true, - asBadge: false, - canEditMembersByModerator: false, - displayOrder: 0, - policies: {}, }); await sleep(); }); diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index db20d407407e..81dcaa5341f7 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -124,6 +124,38 @@ export async function createAccount(host: Host): Promise { }; } +export async function createModerator(host: Host): Promise { + const user = await createAccount(host); + const role = await createRole(host, { + name: 'Moderator', + isModerator: true, + }); + const admin = await fetchAdmin(host); + await admin.client.request('admin/roles/assign', { roleId: role.id, userId: user.id }); + return user; +} + +export async function createRole(host: Host, params: Partial = {}): Promise { + const admin = await fetchAdmin(host); + return await admin.client.request('admin/roles/create', { + name: 'Some role', + description: 'Role for testing', + color: null, + iconUrl: null, + target: 'conditional', + condFormula: {}, + isPublic: true, + isModerator: false, + isAdministrator: false, + isExplorable: true, + asBadge: false, + canEditMembersByModerator: false, + displayOrder: 0, + policies: {}, + ...params, + }); +} + export async function resolveRemoteUser(host: Host, id: string, from: LoginUser): Promise { return new Promise((resolve, reject) => { const uri = `https://${host}/users/${id}`; From c82ba395c7a09a96d554ebe0933d5c1389c4dcee Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sat, 5 Oct 2024 19:11:34 +0900 Subject: [PATCH 103/111] refactor: use existing type --- packages/backend/test-federation/test/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts index 81dcaa5341f7..bd1e3ce8913a 100644 --- a/packages/backend/test-federation/test/utils.ts +++ b/packages/backend/test-federation/test/utils.ts @@ -55,7 +55,7 @@ async function signin(host: Host, params: Misskey.entities.SigninFlowRequest): P return await signin(host, params); } throw err; - }) as Misskey.entities.SigninFlowResponse & { finished: true }; + }) as SigninResponse; } async function createAdmin(host: Host): Promise { From 1afad9ecc00af121e71ae9c5ffdd3f5387b55562 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 6 Oct 2024 14:13:45 +0900 Subject: [PATCH 104/111] refactor: extract duplicate configs to template file --- .../backend/test-federation/compose.a.yml | 104 ++--------------- .../backend/test-federation/compose.b.yml | 107 ++---------------- .../backend/test-federation/compose.tpl.yml | 104 +++++++++++++++++ 3 files changed, 122 insertions(+), 193 deletions(-) create mode 100644 packages/backend/test-federation/compose.tpl.yml diff --git a/packages/backend/test-federation/compose.a.yml b/packages/backend/test-federation/compose.a.yml index 0c2ce2e29492..ea04f0d8a494 100644 --- a/packages/backend/test-federation/compose.a.yml +++ b/packages/backend/test-federation/compose.a.yml @@ -1,6 +1,8 @@ services: a.test: - image: nginx:1.27 + extends: + file: ./compose.tpl.yml + service: nginx depends_on: misskey.a.test: condition: service_healthy @@ -19,17 +21,11 @@ services: source: ./certificates/a.test.key target: /etc/nginx/certificates/a.test.key read_only: true - - type: bind - source: ./certificates/rootCA.crt - target: /etc/nginx/certificates/rootCA.crt - read_only: true - healthcheck: - test: service nginx status - interval: 5s - retries: 20 misskey.a.test: - image: node:20 + extends: + file: ./compose.tpl.yml + service: misskey depends_on: db.a.test: condition: service_healthy @@ -37,106 +33,24 @@ services: condition: service_healthy networks: - internal_network_a - env_file: - - ./.config/docker.env - environment: - - NODE_ENV=production volumes: - type: bind source: ./.config/default.a.yml target: /misskey/.config/default.yml read_only: true - - type: bind - source: ../../../built - target: /misskey/built - read_only: true - - type: bind - source: ../assets - target: /misskey/packages/backend/assets - read_only: true - - type: bind - source: ../built - target: /misskey/packages/backend/built - read_only: true - - type: bind - source: ../migration - target: /misskey/packages/backend/migration - read_only: true - - type: bind - source: ../ormconfig.js - target: /misskey/packages/backend/ormconfig.js - read_only: true - - type: bind - source: ../package.json - target: /misskey/packages/backend/package.json - read_only: true - - type: bind - source: ../../misskey-js/built - target: /misskey/packages/misskey-js/built - read_only: true - - type: bind - source: ../../misskey-js/package.json - target: /misskey/packages/misskey-js/package.json - read_only: true - - type: bind - source: ../../misskey-reversi/built - target: /misskey/packages/misskey-reversi/built - read_only: true - - type: bind - source: ../../misskey-reversi/package.json - target: /misskey/packages/misskey-reversi/package.json - read_only: true - - type: bind - source: ../../../healthcheck.sh - target: /misskey/healthcheck.sh - read_only: true - - type: bind - source: ../../../package.json - target: /misskey/package.json - read_only: true - - type: bind - source: ../../../pnpm-lock.yaml - target: /misskey/pnpm-lock.yaml - read_only: true - - type: bind - source: ../../../pnpm-workspace.yaml - target: /misskey/pnpm-workspace.yaml - read_only: true - - type: bind - source: ./certificates/rootCA.crt - target: /usr/local/share/ca-certificates/rootCA.crt - read_only: true - working_dir: /misskey - command: > - bash -c " - corepack enable && corepack prepare - pnpm -F backend i - pnpm -F misskey-js i - pnpm -F misskey-reversi i - pnpm -F backend migrate - pnpm -F backend start - " - healthcheck: - test: bash /misskey/healthcheck.sh - interval: 5s - retries: 20 db.a.test: - image: postgres:15-alpine + extends: + file: ./compose.tpl.yml + service: db networks: - internal_network_a - env_file: - - ./.config/docker.env volumes: - type: bind source: ./volumes/db.a target: /var/lib/postgresql/data bind: create_host_path: true - healthcheck: - test: pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB - interval: 5s - retries: 20 networks: internal_network_a: diff --git a/packages/backend/test-federation/compose.b.yml b/packages/backend/test-federation/compose.b.yml index 418ff0bb7c31..7bcef848bdf7 100644 --- a/packages/backend/test-federation/compose.b.yml +++ b/packages/backend/test-federation/compose.b.yml @@ -1,6 +1,8 @@ services: b.test: - image: nginx:1.27 + extends: + file: ./compose.tpl.yml + service: nginx depends_on: misskey.b.test: condition: service_healthy @@ -19,127 +21,36 @@ services: source: ./certificates/b.test.key target: /etc/nginx/certificates/b.test.key read_only: true - - type: bind - source: ./certificates/rootCA.crt - target: /etc/nginx/certificates/rootCA.crt - read_only: true - healthcheck: - test: service nginx status - interval: 5s - retries: 20 misskey.b.test: - image: node:20 + extends: + file: ./compose.tpl.yml + service: misskey depends_on: db.b.test: condition: service_healthy redis.test: condition: service_healthy - # avoid conflict for installing dependencies - misskey.a.test: - condition: service_healthy networks: - internal_network_b - env_file: - - ./.config/docker.env - environment: - - NODE_ENV=production volumes: - type: bind source: ./.config/default.b.yml target: /misskey/.config/default.yml read_only: true - - type: bind - source: ../../../built - target: /misskey/built - read_only: true - - type: bind - source: ../assets - target: /misskey/packages/backend/assets - read_only: true - - type: bind - source: ../built - target: /misskey/packages/backend/built - read_only: true - - type: bind - source: ../migration - target: /misskey/packages/backend/migration - read_only: true - - type: bind - source: ../ormconfig.js - target: /misskey/packages/backend/ormconfig.js - read_only: true - - type: bind - source: ../package.json - target: /misskey/packages/backend/package.json - read_only: true - - type: bind - source: ../../misskey-js/built - target: /misskey/packages/misskey-js/built - read_only: true - - type: bind - source: ../../misskey-js/package.json - target: /misskey/packages/misskey-js/package.json - read_only: true - - type: bind - source: ../../misskey-reversi/built - target: /misskey/packages/misskey-reversi/built - read_only: true - - type: bind - source: ../../misskey-reversi/package.json - target: /misskey/packages/misskey-reversi/package.json - read_only: true - - type: bind - source: ../../../healthcheck.sh - target: /misskey/healthcheck.sh - read_only: true - - type: bind - source: ../../../package.json - target: /misskey/package.json - read_only: true - - type: bind - source: ../../../pnpm-lock.yaml - target: /misskey/pnpm-lock.yaml - read_only: true - - type: bind - source: ../../../pnpm-workspace.yaml - target: /misskey/pnpm-workspace.yaml - read_only: true - - type: bind - source: ./certificates/rootCA.crt - target: /usr/local/share/ca-certificates/rootCA.crt - read_only: true - working_dir: /misskey - command: > - bash -c " - corepack enable && corepack prepare - pnpm -F backend i - pnpm -F misskey-js i - pnpm -F misskey-reversi i - pnpm -F backend migrate - pnpm -F backend start - " - healthcheck: - test: bash /misskey/healthcheck.sh - interval: 5s - retries: 20 db.b.test: - image: postgres:15-alpine + extends: + file: ./compose.tpl.yml + service: db networks: - internal_network_b - env_file: - - ./.config/docker.env volumes: - type: bind source: ./volumes/db.b target: /var/lib/postgresql/data bind: create_host_path: true - healthcheck: - test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB" - interval: 5s - retries: 20 networks: internal_network_b: diff --git a/packages/backend/test-federation/compose.tpl.yml b/packages/backend/test-federation/compose.tpl.yml new file mode 100644 index 000000000000..16ed0d061565 --- /dev/null +++ b/packages/backend/test-federation/compose.tpl.yml @@ -0,0 +1,104 @@ +services: + nginx: + image: nginx:1.27 + volumes: + - type: bind + source: ./certificates/rootCA.crt + target: /etc/nginx/certificates/rootCA.crt + read_only: true + healthcheck: + test: service nginx status + interval: 5s + retries: 20 + + misskey: + image: node:20 + env_file: + - ./.config/docker.env + environment: + - NODE_ENV=production + volumes: + - type: bind + source: ../../../built + target: /misskey/built + read_only: true + - type: bind + source: ../assets + target: /misskey/packages/backend/assets + read_only: true + - type: bind + source: ../built + target: /misskey/packages/backend/built + read_only: true + - type: bind + source: ../migration + target: /misskey/packages/backend/migration + read_only: true + - type: bind + source: ../ormconfig.js + target: /misskey/packages/backend/ormconfig.js + read_only: true + - type: bind + source: ../package.json + target: /misskey/packages/backend/package.json + read_only: true + - type: bind + source: ../../misskey-js/built + target: /misskey/packages/misskey-js/built + read_only: true + - type: bind + source: ../../misskey-js/package.json + target: /misskey/packages/misskey-js/package.json + read_only: true + - type: bind + source: ../../misskey-reversi/built + target: /misskey/packages/misskey-reversi/built + read_only: true + - type: bind + source: ../../misskey-reversi/package.json + target: /misskey/packages/misskey-reversi/package.json + read_only: true + - type: bind + source: ../../../healthcheck.sh + target: /misskey/healthcheck.sh + read_only: true + - type: bind + source: ../../../package.json + target: /misskey/package.json + read_only: true + - type: bind + source: ../../../pnpm-lock.yaml + target: /misskey/pnpm-lock.yaml + read_only: true + - type: bind + source: ../../../pnpm-workspace.yaml + target: /misskey/pnpm-workspace.yaml + read_only: true + - type: bind + source: ./certificates/rootCA.crt + target: /usr/local/share/ca-certificates/rootCA.crt + read_only: true + working_dir: /misskey + command: > + bash -c " + corepack enable && corepack prepare + pnpm -F backend i + pnpm -F misskey-js i + pnpm -F misskey-reversi i + pnpm -F backend migrate + pnpm -F backend start + " + healthcheck: + test: bash /misskey/healthcheck.sh + interval: 5s + retries: 20 + + db: + image: postgres:15-alpine + env_file: + - ./.config/docker.env + volumes: + healthcheck: + test: pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB + interval: 5s + retries: 20 From 7983767b70b4cf362066eda68f0b2913fa8dd397 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 6 Oct 2024 14:14:27 +0900 Subject: [PATCH 105/111] fix: typo --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4db924a79a7f..b6184ccb80c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -181,7 +181,7 @@ MK_DEV_PREFER=backend pnpm dev - HMR may not work in some environments such as Windows. ## Testing -You can run non-backend tests by executing following commands +You can run non-backend tests by executing following commands: ```sh pnpm --filter frontend test pnpm --filter misskey-js test From 717cb10a58fd68884754ae6f965e7159c1638bb4 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 6 Oct 2024 14:29:57 +0900 Subject: [PATCH 106/111] fix: avoid conflict --- packages/backend/test-federation/compose.b.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/backend/test-federation/compose.b.yml b/packages/backend/test-federation/compose.b.yml index 7bcef848bdf7..9e3ac91534ed 100644 --- a/packages/backend/test-federation/compose.b.yml +++ b/packages/backend/test-federation/compose.b.yml @@ -31,6 +31,9 @@ services: condition: service_healthy redis.test: condition: service_healthy + # avoid conflict for installing dependencies + misskey.a.test: + condition: service_healthy networks: - internal_network_b volumes: From 90cab5b306ec13f282e22c9220507879868496d3 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 6 Oct 2024 14:38:05 +0900 Subject: [PATCH 107/111] refactor: change container dependency --- packages/backend/test-federation/compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend/test-federation/compose.yml b/packages/backend/test-federation/compose.yml index 51ad01cbd94f..2c550efe106b 100644 --- a/packages/backend/test-federation/compose.yml +++ b/packages/backend/test-federation/compose.yml @@ -10,6 +10,8 @@ services: condition: service_healthy b.test: condition: service_healthy + daemon: + condition: service_healthy environment: - NODE_ENV=development - NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/rootCA.crt @@ -71,9 +73,7 @@ services: daemon: image: node:20 depends_on: - misskey.a.test: - condition: service_healthy - misskey.b.test: + redis.test: condition: service_healthy volumes: - type: bind From 2e845deabf17d485e066ca6abbbf4e3e0b52ccde Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 6 Oct 2024 14:45:29 +0900 Subject: [PATCH 108/111] perf: start misskey parallelly --- packages/backend/test-federation/compose.a.yml | 2 ++ packages/backend/test-federation/compose.b.yml | 5 ++--- .../backend/test-federation/compose.override.yaml | 15 +++++++++++++++ packages/backend/test-federation/compose.tpl.yml | 3 --- packages/backend/test-federation/compose.yml | 12 ++++++++++++ 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/packages/backend/test-federation/compose.a.yml b/packages/backend/test-federation/compose.a.yml index ea04f0d8a494..732d128eda73 100644 --- a/packages/backend/test-federation/compose.a.yml +++ b/packages/backend/test-federation/compose.a.yml @@ -31,6 +31,8 @@ services: condition: service_healthy redis.test: condition: service_healthy + setup: + condition: service_completed_successfully networks: - internal_network_a volumes: diff --git a/packages/backend/test-federation/compose.b.yml b/packages/backend/test-federation/compose.b.yml index 9e3ac91534ed..1eefa4623fc1 100644 --- a/packages/backend/test-federation/compose.b.yml +++ b/packages/backend/test-federation/compose.b.yml @@ -31,9 +31,8 @@ services: condition: service_healthy redis.test: condition: service_healthy - # avoid conflict for installing dependencies - misskey.a.test: - condition: service_healthy + setup: + condition: service_completed_successfully networks: - internal_network_b volumes: diff --git a/packages/backend/test-federation/compose.override.yaml b/packages/backend/test-federation/compose.override.yaml index 4cd38de93640..60a7631ab520 100644 --- a/packages/backend/test-federation/compose.override.yaml +++ b/packages/backend/test-federation/compose.override.yaml @@ -1,4 +1,19 @@ services: + setup: + volumes: + - type: volume + source: node_modules + target: /misskey/node_modules + - type: volume + source: node_modules_backend + target: /misskey/packages/backend/node_modules + - type: volume + source: node_modules_misskey-js + target: /misskey/packages/misskey-js/node_modules + - type: volume + source: node_modules_misskey-reversi + target: /misskey/packages/misskey-reversi/node_modules + tester: networks: external_network: diff --git a/packages/backend/test-federation/compose.tpl.yml b/packages/backend/test-federation/compose.tpl.yml index 16ed0d061565..8c38f16919f8 100644 --- a/packages/backend/test-federation/compose.tpl.yml +++ b/packages/backend/test-federation/compose.tpl.yml @@ -82,9 +82,6 @@ services: command: > bash -c " corepack enable && corepack prepare - pnpm -F backend i - pnpm -F misskey-js i - pnpm -F misskey-reversi i pnpm -F backend migrate pnpm -F backend start " diff --git a/packages/backend/test-federation/compose.yml b/packages/backend/test-federation/compose.yml index 2c550efe106b..4b1d9df7a78c 100644 --- a/packages/backend/test-federation/compose.yml +++ b/packages/backend/test-federation/compose.yml @@ -3,6 +3,18 @@ include: - ./compose.b.yml services: + setup: + extends: + file: ./compose.tpl.yml + service: misskey + command: > + bash -c " + corepack enable && corepack prepare + pnpm -F backend i + pnpm -F misskey-js i + pnpm -F misskey-reversi i + " + tester: image: node:20 depends_on: From a31d4834de911f3165c64f1fb5fd95f70b7926fd Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 6 Oct 2024 14:49:28 +0900 Subject: [PATCH 109/111] fix: remove dependency --- packages/backend/test-federation/compose.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/backend/test-federation/compose.yml b/packages/backend/test-federation/compose.yml index 4b1d9df7a78c..62d7e977c04e 100644 --- a/packages/backend/test-federation/compose.yml +++ b/packages/backend/test-federation/compose.yml @@ -22,8 +22,6 @@ services: condition: service_healthy b.test: condition: service_healthy - daemon: - condition: service_healthy environment: - NODE_ENV=development - NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/rootCA.crt From 0c607d091d09a95b002107eb384cbfe5ef1bcf8d Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:51:45 +0900 Subject: [PATCH 110/111] chore(backend): add typecheck --- packages/backend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index 820aa6a7f92f..bf1e83eca42c 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -19,7 +19,7 @@ "watch": "node ./scripts/watch.mjs", "restart": "pnpm build && pnpm start", "dev": "node ./scripts/dev.mjs", - "typecheck": "tsc --noEmit && tsc -p test --noEmit", + "typecheck": "tsc --noEmit && tsc -p test --noEmit && tsc -p test-federation --noEmit", "eslint": "eslint --quiet \"src/**/*.ts\"", "lint": "pnpm typecheck && pnpm eslint", "jest": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --config jest.config.unit.cjs", From ef71ae0d9de3732ccf30da0787c179a5c313be0b Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 9 Oct 2024 19:12:21 +0900 Subject: [PATCH 111/111] test: add check for #14728 --- packages/backend/test-federation/test/user.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index f660d6d89f0e..76605e61d427 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -420,6 +420,13 @@ describe('User', () => { await bAdmin.client.request('admin/delete-account', { userId: aliceInB.id }); await sleep(); + /** + * FIXME: remote account is not deleted! + * @see https://github.com/misskey-dev/misskey/issues/14728 + */ + const deletedAlice = await bob.client.request('users/show', { userId: aliceInB.id }); + assert(deletedAlice.id, aliceInB.id); + // TODO: why still following relation? const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 1);