1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-05-22 19:54:03 +02:00

Merge branch 'develop' into room

This commit is contained in:
syuilo
2026-04-21 06:13:42 +09:00
112 changed files with 756 additions and 3071 deletions

View File

@@ -19,7 +19,6 @@
"editorconfig.editorconfig", "editorconfig.editorconfig",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"Vue.volar", "Vue.volar",
"Orta.vscode-jest",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"mrmlnc.vscode-json5" "mrmlnc.vscode-json5"
] ]

View File

@@ -34,9 +34,6 @@ updates:
patterns: patterns:
- "storybook*" - "storybook*"
- "@storybook/*" - "@storybook/*"
swc-core:
patterns:
- "@swc/core*"
typescript-eslint: typescript-eslint:
patterns: patterns:
- "@typescript-eslint/*" - "@typescript-eslint/*"

View File

@@ -1,11 +1,10 @@
## 2026.4.0 ## 2026.4.0
### General ### General
- - Enhance: アバターデコレーションにカテゴリを設定できるように
### Client ### Client
- Enhance: チャンネル指定リノートでリノート先のチャンネルに移動できるように - Enhance: チャンネル指定リノートでリノート先のチャンネルに移動できるように
- Enhance: アバターデコレーションにカテゴリを設定できるように
- Fix: 一部のページ内リンクが正しく動作しない問題を修正 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
- Fix: ドライブへの画像アップロード時にファイル名の変更が無視される不具合を修正 - Fix: ドライブへの画像アップロード時にファイル名の変更が無視される不具合を修正
- Fix: 連合が無効化されたサーバーで一部の設定項目が空欄で表示される問題を修正 - Fix: 連合が無効化されたサーバーで一部の設定項目が空欄で表示される問題を修正
@@ -15,6 +14,7 @@
- Enhance: 起動の高速化 - Enhance: 起動の高速化
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1410) (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1410)
- Enhance: バックエンドの開発モード時の安定性向上 - Enhance: バックエンドの開発モード時の安定性向上
- Enhance: バックエンドビルド・テスト時に使用する依存関係の整理swc/esbuild→Rolldown, Jest→Vitest
- Fix: ファイルシステムを用いる処理におけるパスの取り扱いを改善 - Fix: ファイルシステムを用いる処理におけるパスの取り扱いを改善
- Fix: `/api-doc` にアクセスできない問題を修正 - Fix: `/api-doc` にアクセスできない問題を修正
- Fix: support `alsoKnownAs` from remote actors as either array or unwrapped singleton - Fix: support `alsoKnownAs` from remote actors as either array or unwrapped singleton

View File

@@ -44,8 +44,8 @@
"cy:run": "pnpm cypress run", "cy:run": "pnpm cypress run",
"e2e": "pnpm start-server-and-test start:test http://localhost:61812 cy:run", "e2e": "pnpm start-server-and-test start:test http://localhost:61812 cy:run",
"e2e-dev-container": "ncp ./.config/cypress-devcontainer.yml ./.config/test.yml && pnpm start-server-and-test start:test http://localhost:61812 cy:run", "e2e-dev-container": "ncp ./.config/cypress-devcontainer.yml ./.config/test.yml && pnpm start-server-and-test start:test http://localhost:61812 cy:run",
"jest": "cd packages/backend && pnpm jest", "backend-unit-test": "cd packages/backend && pnpm test",
"jest-and-coverage": "cd packages/backend && pnpm jest-and-coverage", "backend-unit-test-and-coverage": "cd packages/backend && pnpm test-and-coverage",
"test": "pnpm -r test", "test": "pnpm -r test",
"test-and-coverage": "pnpm -r test-and-coverage", "test-and-coverage": "pnpm -r test-and-coverage",
"clean": "node scripts/clean.mjs", "clean": "node scripts/clean.mjs",

View File

@@ -1,29 +0,0 @@
{
"$schema": "https://swc.rs/schema.json",
"jsc": {
"parser": {
"syntax": "typescript",
"jsx": true,
"dynamicImport": true,
"decorators": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true,
"react": {
"runtime": "automatic",
"importSource": "@kitajs/html"
}
},
"experimental": {
"keepImportAssertions": true
},
"baseUrl": "src",
"paths": {
"@/*": ["*"]
},
"target": "es2022"
},
"minify": false,
"sourceMaps": "inline"
}

View File

@@ -1,220 +0,0 @@
/*
* For a detailed explanation regarding each configuration property and type check, visit:
* https://jestjs.io/docs/en/configuration.html
*/
module.exports = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "C:\\Users\\ai\\AppData\\Local\\Temp\\jest",
// Automatically clear mock calls and instances between every test
// clearMocks: false,
// Indicates whether the coverage information should be collected while executing the test
// collectCoverage: false,
// An array of glob patterns indicating a set of files for which coverage information should be collected
collectCoverageFrom: ['src/**/*.ts', '!src/**/*.test.ts'],
// The directory where Jest should output its coverage files
coverageDirectory: "coverage",
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// Indicates which provider should be used to instrument code for coverage
coverageProvider: "v8",
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
// A path to a custom dependency extractor
// dependencyExtractor: undefined,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: undefined,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: undefined,
// A set of global variables that need to be available in all test environments
globals: {
},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "json",
// "jsx",
// "ts",
// "tsx",
// "node"
// ],
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
moduleNameMapper: {
// Do not resolve .wasm.js to .wasm by the rule below
'^(.+)\\.wasm\\.js$': '$1.wasm.js',
// SWC converts @/foo/bar.js to `../../src/foo/bar.js`, and then this rule
// converts it again to `../../src/foo/bar` which then can be resolved to
// `.ts` files.
// See https://github.com/swc-project/jest/issues/64#issuecomment-1029753225
// TODO: Use `--allowImportingTsExtensions` on TypeScript 5.0 so that we can
// directly import `.ts` files without this hack.
'^((?:\\.{1,2}|[A-Z:])*/.*)\\.js$': '$1',
},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
//preset: "ts-jest/presets/js-with-ts-esm",
// Run tests from one or more projects
// projects: undefined,
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state between every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: './jest-resolver.cjs',
// Automatically restore mock state between every test
restoreMocks: true,
// The root directory that Jest should scan for tests and modules within
// rootDir: undefined,
// A list of paths to directories that Jest should use to search for files in
roots: [
"<rootDir>"
],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
// The number of seconds after which a test is considered as slow and reported as such in the results.
// slowTestThreshold: 5,
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
testEnvironment: "node",
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
// Adds a location field to test results
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
testMatch: [
"<rootDir>/test/unit/**/*.ts",
"<rootDir>/src/**/*.test.ts",
],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
// This option allows the use of a custom results processor
// testResultsProcessor: undefined,
// This option allows use of a custom test runner
// testRunner: "jasmine2",
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
// testURL: "http://localhost",
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
// timers: "real",
// A map from regular expressions to paths to transformers
transform: {
"^.+\\.(t|j)sx?$": ["@swc/jest"],
},
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "\\\\node_modules\\\\",
// "\\.pnp\\.[^\\\\]+$"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: undefined,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
extensionsToTreatAsEsm: ['.ts', '.tsx'],
testTimeout: 60000,
// Let Jest kill the test worker whenever it grows too much
// (It seems there's a known memory leak issue in Node.js' vm.Script used by Jest)
// https://github.com/facebook/jest/issues/11956
maxWorkers: 1, // Make it use worker (that can be killed and restarted)
logHeapUsage: true, // To debug when out-of-memory happens on CI
workerIdleMemoryLimit: '1GiB', // Limit the worker to 1GB (GitHub Workflows dies at 2GB)
maxConcurrency: 32,
};

View File

@@ -1,15 +0,0 @@
/*
* 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,
globalSetup: "<rootDir>/built-test/entry.js",
setupFilesAfterEnv: ["<rootDir>/test/jest.setup.ts"],
testMatch: [
"<rootDir>/test/e2e/**/*.ts",
],
};

View File

@@ -1,13 +0,0 @@
/*
* 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: [
'<rootDir>/test-federation/test/**/*.test.ts',
],
};

View File

@@ -1,15 +0,0 @@
/*
* 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,
globalSetup: "<rootDir>/test/jest.setup.unit.cjs",
testMatch: [
"<rootDir>/test/unit/**/*.ts",
"<rootDir>/src/**/*.test.ts",
],
};

View File

@@ -1,31 +0,0 @@
#!/usr/bin/env node
import child_process from 'node:child_process';
import path from 'node:path';
import url from 'node:url';
import semver from 'semver';
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const args = [];
args.push(...[
...semver.satisfies(process.version, '^20.17.0 || ^22.0.0 || ^24.10.0') ? ['--no-experimental-require-module'] : [],
'--experimental-vm-modules',
'--experimental-import-meta-resolve',
path.join(__dirname, 'node_modules/jest/bin/jest.js'),
...process.argv.slice(2),
]);
const child = child_process.spawn(process.execPath, args, { stdio: 'inherit' });
child.on('error', (err) => {
console.error('Failed to start Jest:', err);
process.exit(1);
});
child.on('exit', (code, signal) => {
if (code === null) {
process.exit(128 + signal);
} else {
process.exit(code);
}
});

View File

@@ -17,8 +17,7 @@
"compile-config": "node ./scripts/compile_config.js", "compile-config": "node ./scripts/compile_config.js",
"build": "rolldown -c", "build": "rolldown -c",
"build:unit": "rolldown -c --sourcemap", "build:unit": "rolldown -c --sourcemap",
"build:e2e": "swc src -d src-js -D --strip-leading-paths && swc test-server -d built-test -D --config-file test-server/.swcrc --strip-leading-paths", "build:e2e": "rolldown -c --e2e",
"watch:swc": "swc src -d built -D -w --strip-leading-paths",
"build:tsc": "tsgo -p tsconfig.json && tsc-alias -p tsconfig.json", "build:tsc": "tsgo -p tsconfig.json && tsc-alias -p tsconfig.json",
"watch": "pnpm compile-config && node ./scripts/watch.mjs", "watch": "pnpm compile-config && node ./scripts/watch.mjs",
"restart": "pnpm build && pnpm start", "restart": "pnpm build && pnpm start",
@@ -26,33 +25,15 @@
"typecheck": "tsgo --noEmit && tsgo -p test --noEmit && tsgo -p test-federation --noEmit", "typecheck": "tsgo --noEmit && tsgo -p test --noEmit && tsgo -p test-federation --noEmit",
"eslint": "eslint --quiet \"{src,test-federation}/**/*.ts\"", "eslint": "eslint --quiet \"{src,test-federation}/**/*.ts\"",
"lint": "pnpm typecheck && pnpm eslint", "lint": "pnpm typecheck && pnpm eslint",
"jest": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --forceExit --config jest.config.unit.cjs", "test": "pnpm build:unit && cross-env NODE_ENV=test pnpm compile-config && vitest --config vitest.config.unit.ts",
"jest:e2e": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --forceExit --config jest.config.e2e.cjs", "test:e2e": "pnpm build:e2e && cross-env NODE_ENV=test pnpm compile-config && vitest --config vitest.config.e2e.ts",
"jest:fed": "pnpm compile-config && node ./jest.js --forceExit --config jest.config.fed.cjs", "test:fed": "cross-env NODE_ENV=test pnpm compile-config && vitest --config vitest.config.fed.ts",
"jest-and-coverage": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --coverage --forceExit --config jest.config.unit.cjs", "test-and-coverage": "pnpm build:unit && cross-env NODE_ENV=test pnpm compile-config && vitest --coverage --config vitest.config.unit.ts",
"jest-and-coverage:e2e": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --coverage --forceExit --config jest.config.e2e.cjs", "test-and-coverage:e2e": "pnpm build:e2e && cross-env NODE_ENV=test pnpm compile-config && vitest --coverage --config vitest.config.e2e.ts",
"jest-clear": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --clearCache",
"test": "pnpm build:unit && pnpm jest",
"test:e2e": "pnpm build:e2e && pnpm jest:e2e",
"test:fed": "pnpm jest:fed",
"test-and-coverage": "pnpm build:unit && pnpm jest-and-coverage",
"test-and-coverage:e2e": "pnpm build:e2e && pnpm jest-and-coverage:e2e",
"check-migrations": "node scripts/check_migrations_clean.js", "check-migrations": "node scripts/check_migrations_clean.js",
"generate-api-json": "pnpm compile-config && node ./scripts/generate_api_json.js" "generate-api-json": "pnpm compile-config && node ./scripts/generate_api_json.js"
}, },
"optionalDependencies": { "optionalDependencies": {
"@swc/core-android-arm64": "1.3.11",
"@swc/core-darwin-arm64": "1.15.24",
"@swc/core-darwin-x64": "1.15.24",
"@swc/core-freebsd-x64": "1.3.11",
"@swc/core-linux-arm-gnueabihf": "1.15.24",
"@swc/core-linux-arm64-gnu": "1.15.24",
"@swc/core-linux-arm64-musl": "1.15.24",
"@swc/core-linux-x64-gnu": "1.15.24",
"@swc/core-linux-x64-musl": "1.15.24",
"@swc/core-win32-arm64-msvc": "1.15.24",
"@swc/core-win32-ia32-msvc": "1.15.24",
"@swc/core-win32-x64-msvc": "1.15.24",
"@tensorflow/tfjs": "4.22.0", "@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0", "@tensorflow/tfjs-node": "4.22.0",
"bufferutil": "4.1.0", "bufferutil": "4.1.0",
@@ -95,8 +76,6 @@
"@simplewebauthn/server": "13.3.0", "@simplewebauthn/server": "13.3.0",
"@sinonjs/fake-timers": "15.3.0", "@sinonjs/fake-timers": "15.3.0",
"@smithy/node-http-handler": "4.5.2", "@smithy/node-http-handler": "4.5.2",
"@swc/cli": "0.8.1",
"@swc/core": "1.15.24",
"@twemoji/parser": "16.0.0", "@twemoji/parser": "16.0.0",
"accepts": "1.3.8", "accepts": "1.3.8",
"ajv": "8.18.0", "ajv": "8.18.0",
@@ -178,13 +157,11 @@
"xev": "3.0.2" "xev": "3.0.2"
}, },
"devDependencies": { "devDependencies": {
"@jest/globals": "29.7.0",
"@kitajs/ts-html-plugin": "4.1.4", "@kitajs/ts-html-plugin": "4.1.4",
"@nestjs/platform-express": "11.1.18", "@nestjs/platform-express": "11.1.18",
"@rollup/plugin-esm-shim": "0.1.8", "@rollup/plugin-esm-shim": "0.1.8",
"@sentry/vue": "10.47.0", "@sentry/vue": "10.47.0",
"@simplewebauthn/types": "12.0.0", "@simplewebauthn/types": "12.0.0",
"@swc/jest": "0.2.39",
"@types/accepts": "1.3.7", "@types/accepts": "1.3.7",
"@types/archiver": "7.0.0", "@types/archiver": "7.0.0",
"@types/body-parser": "1.19.6", "@types/body-parser": "1.19.6",
@@ -192,7 +169,7 @@
"@types/content-disposition": "0.5.9", "@types/content-disposition": "0.5.9",
"@types/fluent-ffmpeg": "2.1.28", "@types/fluent-ffmpeg": "2.1.28",
"@types/http-link-header": "1.0.7", "@types/http-link-header": "1.0.7",
"@types/jest": "29.5.14", "@types/js-yaml": "4.0.9",
"@types/jsonld": "1.5.15", "@types/jsonld": "1.5.15",
"@types/mime-types": "3.0.1", "@types/mime-types": "3.0.1",
"@types/ms": "2.1.0", "@types/ms": "2.1.0",
@@ -217,19 +194,20 @@
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.58.1", "@typescript-eslint/eslint-plugin": "8.58.1",
"@typescript-eslint/parser": "8.58.1", "@typescript-eslint/parser": "8.58.1",
"@vitest/coverage-v8": "4.1.4",
"aws-sdk-client-mock": "4.1.0", "aws-sdk-client-mock": "4.1.0",
"cbor": "10.0.12", "cbor": "10.0.12",
"cross-env": "10.1.0", "cross-env": "10.1.0",
"eslint-plugin-import": "2.32.0", "eslint-plugin-import": "2.32.0",
"execa": "9.6.1", "execa": "9.6.1",
"fkill": "10.0.3", "fkill": "10.0.3",
"jest": "29.7.0",
"jest-mock": "29.7.0",
"js-yaml": "4.1.1", "js-yaml": "4.1.1",
"pid-port": "2.1.1", "pid-port": "2.1.1",
"rolldown": "1.0.0-rc.15", "rolldown": "1.0.0-rc.15",
"simple-oauth2": "5.1.0", "simple-oauth2": "5.1.0",
"supertest": "7.2.2", "supertest": "7.2.2",
"vite": "8.0.8" "vite": "8.0.8",
"vitest": "4.1.4",
"vitest-mock-extended": "3.1.1"
} }
} }

View File

@@ -53,6 +53,7 @@ function backendDevServerPlugin(): Plugin {
export default defineConfig((args) => { export default defineConfig((args) => {
const isWatchMode = args.watch != null && args.watch !== 'false'; const isWatchMode = args.watch != null && args.watch !== 'false';
const isE2E = args.e2e != null && args.e2e !== 'false';
// 通常のビルド時にexternalとするモジュール // 通常のビルド時にexternalとするモジュール
const externalModules: ExternalOption = [ const externalModules: ExternalOption = [
@@ -75,33 +76,52 @@ export default defineConfig((args) => {
'oauth2orize', 'oauth2orize',
]; ];
return { if (isE2E) {
input: [ return {
'./src/boot/entry.ts', input: './test-server/entry.ts',
'./src/boot/cli.ts', platform: 'node',
'./src/config.ts', tsconfig: './test-server/tsconfig.json',
'./src/postgres.ts', plugins: [
'./src/server/api/openapi/gen-spec.ts', esmShim(),
], ],
platform: 'node', output: {
tsconfig: true, keepNames: true,
plugins: [ sourcemap: true,
esmShim(), dir: './built-test',
(isWatchMode ? backendDevServerPlugin() : undefined), cleanDir: true,
], format: 'esm',
output: { },
keepNames: true, external: externalModules,
minify: !isWatchMode, };
sourcemap: isWatchMode, } else {
dir: './built', return {
cleanDir: !isWatchMode, input: [
format: 'esm', './src/boot/entry.ts',
}, './src/boot/cli.ts',
watch: { './src/config.ts',
include: ['src/**/*.{ts,js,mjs,cjs,tsx,json}'], './src/postgres.ts',
clearScreen: false, './src/server/api/openapi/gen-spec.ts',
}, ],
// ビルドの高速化のために、watchモードのときは外部モジュールは全てバンドルしないようにする platform: 'node',
external: isWatchMode ? /^(?!@\/)[^.\/](?!:[\/\\])/ : externalModules, tsconfig: true,
}; plugins: [
esmShim(),
(isWatchMode ? backendDevServerPlugin() : undefined),
],
output: {
keepNames: true,
minify: !isWatchMode,
sourcemap: isWatchMode,
dir: './built',
cleanDir: !isWatchMode,
format: 'esm',
},
watch: {
include: ['src/**/*.{ts,js,mjs,cjs,tsx,json}'],
clearScreen: false,
},
// ビルドの高速化のために、watchモードのときは外部モジュールは全てバンドルしないようにする
external: isWatchMode ? /^(?!@\/)[^.\/](?!:[\/\\])/ : externalModules,
};
}
}); });

View File

@@ -5,7 +5,13 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.js';
import endpoints from '../endpoints.js';
// 循環参照を回避
let endpointsPromise: Promise<typeof import('../endpoints.js').default> | undefined;
function getEndpoints() {
return endpointsPromise ??= import('../endpoints.js').then(module => module.default);
}
export const meta = { export const meta = {
requireCredential: false, requireCredential: false,
@@ -43,6 +49,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
constructor( constructor(
) { ) {
super(meta, paramDef, async (ps) => { super(meta, paramDef, async (ps) => {
const endpoints = await getEndpoints();
const ep = endpoints.find(x => x.name === ps.endpoint); const ep = endpoints.find(x => x.name === ps.endpoint);
if (ep == null) return null; if (ep == null) return null;
return { return {

View File

@@ -5,7 +5,13 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.js';
import endpoints from '../endpoints.js';
// 循環参照を回避
let endpointsPromise: Promise<typeof import('../endpoints.js').default> | undefined;
function getEndpoints() {
return endpointsPromise ??= import('../endpoints.js').then(module => module.default);
}
export const meta = { export const meta = {
requireCredential: false, requireCredential: false,
@@ -39,6 +45,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
constructor( constructor(
) { ) {
super(meta, paramDef, async () => { super(meta, paramDef, async () => {
const endpoints = await getEndpoints();
return endpoints.map(x => x.name); return endpoints.map(x => x.name);
}); });
} }

View File

@@ -8,7 +8,7 @@ process.env.NODE_ENV = 'test';
import { readFile } from 'node:fs/promises'; import { readFile } from 'node:fs/promises';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path'; import { dirname } from 'node:path';
import { describe, test, expect } from '@jest/globals'; import { describe, test, expect } from 'vitest';
import { getValidator } from '../../../../../test/prelude/get-api-validator.js'; import { getValidator } from '../../../../../test/prelude/get-api-validator.js';
import { paramDef } from './create.js'; import { paramDef } from './create.js';

View File

@@ -5,6 +5,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import { describe, test, expect } from 'vitest';
import { getValidator } from '../../../../../test/prelude/get-api-validator.js'; import { getValidator } from '../../../../../test/prelude/get-api-validator.js';
import { paramDef } from './show.js'; import { paramDef } from './show.js';

View File

@@ -214,7 +214,7 @@ export class ClientServerService {
//#region vite assets //#region vite assets
if (this.config.frontendEmbedManifestExists) { if (this.config.frontendEmbedManifestExists) {
console.log(`[ClientServerService] Using built frontend vite assets. ${this.frontendViteOut}`); this.clientLoggerService.logger.info(`[ClientServerService] Using built frontend vite assets. ${this.frontendViteOut}`);
fastify.register((fastify, options, done) => { fastify.register((fastify, options, done) => {
fastify.register(fastifyStatic, { fastify.register(fastifyStatic, {
root: this.frontendViteOut, root: this.frontendViteOut,

View File

@@ -43,16 +43,12 @@ services:
target: /misskey/packages/backend/test-federation/test target: /misskey/packages/backend/test-federation/test
read_only: true read_only: true
- type: bind - type: bind
source: ../jest.config.cjs source: ../vitest.config.ts
target: /misskey/packages/backend/jest.config.cjs target: /misskey/packages/backend/vitest.config.ts
read_only: true read_only: true
- type: bind - type: bind
source: ../jest.config.fed.cjs source: ../vitest.config.fed.ts
target: /misskey/packages/backend/jest.config.fed.cjs target: /misskey/packages/backend/vitest.config.fed.ts
read_only: true
- type: bind
source: ../jest.js
target: /misskey/packages/backend/jest.js
read_only: true read_only: true
- type: bind - type: bind
source: ../scripts/compile_config.js source: ../scripts/compile_config.js

View File

@@ -1,3 +1,4 @@
import { describe, test, beforeAll } from 'vitest';
import { rejects, strictEqual } from 'node:assert'; import { rejects, strictEqual } from 'node:assert';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { createAccount, createModerator, resolveRemoteUser, sleep, type LoginUser } from './utils.js'; import { createAccount, createModerator, resolveRemoteUser, sleep, type LoginUser } from './utils.js';

View File

@@ -1,3 +1,4 @@
import { describe, test, beforeAll } from 'vitest';
import { deepStrictEqual, rejects, strictEqual } from 'node:assert'; import { deepStrictEqual, rejects, strictEqual } from 'node:assert';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { assertNotificationReceived, createAccount, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; import { assertNotificationReceived, createAccount, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js';

View File

@@ -1,3 +1,4 @@
import { describe, test, beforeAll } from 'vitest';
import assert, { strictEqual } from 'node:assert'; import assert, { strictEqual } from 'node:assert';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep, uploadFile } from './utils.js'; import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep, uploadFile } from './utils.js';

View File

@@ -1,3 +1,4 @@
import { describe, test, beforeAll } from 'vitest';
import assert, { deepStrictEqual, strictEqual } from 'assert'; import assert, { deepStrictEqual, strictEqual } from 'assert';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { addCustomEmoji, createAccount, type LoginUser, resolveRemoteUser, sleep } from './utils.js'; import { addCustomEmoji, createAccount, type LoginUser, resolveRemoteUser, sleep } from './utils.js';

View File

@@ -1,3 +1,4 @@
import { describe, test, beforeAll } from 'vitest';
import assert, { strictEqual } from 'node:assert'; import assert, { strictEqual } from 'node:assert';
import { createAccount, type LoginUser, sleep } from './utils.js'; import { createAccount, type LoginUser, sleep } from './utils.js';

View File

@@ -1,3 +1,4 @@
import { describe, test, beforeAll, afterAll } from 'vitest';
import assert, { rejects, strictEqual } from 'node:assert'; import assert, { rejects, strictEqual } from 'node:assert';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { addCustomEmoji, createAccount, createModerator, deepStrictEqualWithExcludedFields, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep, uploadFile } from './utils.js'; import { addCustomEmoji, createAccount, createModerator, deepStrictEqualWithExcludedFields, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep, uploadFile } from './utils.js';
@@ -214,7 +215,7 @@ describe('Note', () => {
* @see https://github.com/misskey-dev/misskey/issues/15548 * @see https://github.com/misskey-dev/misskey/issues/15548
*/ */
describe('To only resolved and not followed user', () => { describe('To only resolved and not followed user', () => {
test.failing('Check', async () => { test.skip('Check', async () => {
const note = (await bob.client.request('notes/create', { text: 'I\'m Bob.' })).createdNote; const note = (await bob.client.request('notes/create', { text: 'I\'m Bob.' })).createdNote;
const noteInA = await resolveRemoteNote('b.test', note.id, alice); const noteInA = await resolveRemoteNote('b.test', note.id, alice);
await sleep(); await sleep();
@@ -254,7 +255,7 @@ describe('Note', () => {
* FIXME: implement soft deletion as well as user? * FIXME: implement soft deletion as well as user?
* @see https://github.com/misskey-dev/misskey/issues/11437 * @see https://github.com/misskey-dev/misskey/issues/11437
*/ */
test.failing('Not found even if resolve again', async () => { test.skip('Not found even if resolve again', async () => {
const noteInB = await resolveRemoteNote('a.test', note.id, bob); const noteInB = await resolveRemoteNote('a.test', note.id, bob);
await rejects( await rejects(
async () => await bob.client.request('notes/show', { noteId: noteInB.id }), async () => await bob.client.request('notes/show', { noteId: noteInB.id }),

View File

@@ -1,3 +1,4 @@
import { describe, test, beforeAll, afterAll } from 'vitest';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { assertNotificationReceived, createAccount, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; import { assertNotificationReceived, createAccount, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js';

View File

@@ -1,3 +1,4 @@
import { describe, test, beforeAll, afterAll } from 'vitest';
import { strictEqual } from 'assert'; import { strictEqual } from 'assert';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { createAccount, fetchAdmin, isNoteUpdatedEventFired, isFired, type LoginUser, type Request, resolveRemoteUser, sleep, createRole } from './utils.js'; import { createAccount, fetchAdmin, isNoteUpdatedEventFired, isFired, type LoginUser, type Request, resolveRemoteUser, sleep, createRole } from './utils.js';
@@ -117,7 +118,7 @@ describe('Timeline', () => {
* FIXME: can receive this * FIXME: can receive this
* @see https://github.com/misskey-dev/misskey/issues/14083 * @see https://github.com/misskey-dev/misskey/issues/14083
*/ */
test.failing('Don\'t receive remote followee\'s invisible and mentioned specified-only Note', async () => { test.skip('Don\'t receive remote followee\'s invisible and mentioned specified-only Note', async () => {
await postAndCheckReception(homeTimeline, false, { text: `@${bob.username}@b.test Hello`, visibility: 'specified' }); await postAndCheckReception(homeTimeline, false, { text: `@${bob.username}@b.test Hello`, visibility: 'specified' });
}); });
@@ -125,7 +126,7 @@ describe('Timeline', () => {
* FIXME: cannot receive this * FIXME: cannot receive this
* @see https://github.com/misskey-dev/misskey/issues/14084 * @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 () => { test.skip('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; const note = (await alice.client.request('notes/create', { text: 'a', visibility: 'specified' })).createdNote;
await postAndCheckReception(homeTimeline, true, { replyId: note.id, visibility: 'specified', visibleUserIds: [bobInA.id] }); await postAndCheckReception(homeTimeline, true, { replyId: note.id, visibility: 'specified', visibleUserIds: [bobInA.id] });
}); });

View File

@@ -1,3 +1,4 @@
import { describe, test, beforeAll } from 'vitest';
import assert, { rejects, strictEqual } from 'node:assert'; import assert, { rejects, strictEqual } from 'node:assert';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js';

View File

@@ -1,23 +0,0 @@
{
"$schema": "https://swc.rs/schema.json",
"jsc": {
"parser": {
"syntax": "typescript",
"dynamicImport": true,
"decorators": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true
},
"experimental": {
"keepImportAssertions": true
},
"baseUrl": "../src-js",
"paths": {
"@/*": ["*"]
},
"target": "es2022"
},
"minify": false
}

View File

@@ -19,7 +19,7 @@ let serverService: ServerService;
/** /**
* テスト用のサーバインスタンスを起動する * テスト用のサーバインスタンスを起動する
*/ */
async function launch() { export async function setup() {
await killTestServer(); await killTestServer();
console.log('starting application...'); console.log('starting application...');
@@ -38,6 +38,15 @@ async function launch() {
console.log('application initialized.'); console.log('application initialized.');
} }
/**
* テスト用のサーバインスタンスを停止する
*/
export async function teardown() {
await serverService.dispose();
await app.close();
await killTestServer();
}
/** /**
* 既に重複したポートで待ち受けしているサーバがある場合はkillする * 既に重複したポートで待ち受けしているサーバがある場合はkillする
*/ */
@@ -94,5 +103,3 @@ async function startControllerEndpoints(port = config.port + 1000) {
await fastify.listen({ port: port, host: 'localhost' }); await fastify.listen({ port: port, host: 'localhost' });
} }
export default launch;

View File

@@ -25,7 +25,6 @@
"isolatedModules": true, "isolatedModules": true,
"jsx": "react-jsx", "jsx": "react-jsx",
"jsxImportSource": "@kitajs/html", "jsxImportSource": "@kitajs/html",
"rootDir": "../src",
"paths": { "paths": {
"@/*": ["../src/*"] "@/*": ["../src/*"]
}, },

View File

@@ -20,6 +20,7 @@ import type {
RegistrationResponseJSON, RegistrationResponseJSON,
} from '@simplewebauthn/types'; } from '@simplewebauthn/types';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
import { describe, beforeAll, test } from 'vitest';
describe('2要素認証', () => { describe('2要素認証', () => {
let alice: misskey.entities.SignupResponse; let alice: misskey.entities.SignupResponse;

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, beforeAll, beforeEach, test } from 'vitest';
import { import {
api, api,
failedApiCall, failedApiCall,

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, beforeAll, beforeEach, test } from 'vitest';
import { UserToken, api, post, signup } from '../utils.js'; import { UserToken, api, post, signup } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';

View File

@@ -6,7 +6,8 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { IncomingMessage } from 'http'; import { describe, beforeAll, test } from 'vitest';
import { IncomingMessage } from 'node:http';
import { import {
api, api,
connectStream, connectStream,

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, beforeAll, test } from 'vitest';
import { api, castAsError, post, signup } from '../utils.js'; import { api, castAsError, post, signup } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, beforeAll, beforeEach, afterEach, test } from 'vitest';
import { DEFAULT_POLICIES } from '@/core/RoleService.js'; import { DEFAULT_POLICIES } from '@/core/RoleService.js';
import { api, ApiRequest, failedApiCall, hiddenNote, post, signup, successfulApiCall } from '../utils.js'; import { api, ApiRequest, failedApiCall, hiddenNote, post, signup, successfulApiCall } from '../utils.js';
import type * as Misskey from 'misskey-js'; import type * as Misskey from 'misskey-js';
@@ -176,7 +177,9 @@ describe('クリップ', () => {
{ label: 'descriptionがnull', parameters: { description: null } }, { label: 'descriptionがnull', parameters: { description: null } },
{ label: 'descriptionが最大長', parameters: { description: 'a'.repeat(2048) } }, { label: 'descriptionが最大長', parameters: { description: 'a'.repeat(2048) } },
]; ];
test.each(createClipAllowedPattern)('の作成は$labelでもできる', async ({ parameters }) => await create(parameters)); test.each(createClipAllowedPattern)('の作成は$labelでもできる', async ({ parameters }) => {
await create(parameters);
});
const createClipDenyPattern = [ const createClipDenyPattern = [
{ label: 'nameがnull', parameters: { name: null } }, { label: 'nameがnull', parameters: { name: null } },
@@ -233,11 +236,13 @@ describe('クリップ', () => {
assert.strictEqual(res.isFavorited, false); assert.strictEqual(res.isFavorited, false);
}); });
test.each(createClipAllowedPattern)('の更新は$labelでもできる', async ({ parameters }) => await update({ test.each(createClipAllowedPattern)('の更新は$labelでもできる', async ({ parameters }) => {
clipId: (await create()).id, await update({
name: 'updated', clipId: (await create()).id,
...parameters, name: 'updated',
})); ...parameters,
});
});
test.each([ test.each([
{ label: 'clipIdがnull', parameters: { clipId: null } }, { label: 'clipIdがnull', parameters: { clipId: null } },

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, beforeAll, test } from 'vitest';
import { api, makeStreamCatcher, post, signup, uploadFile } from '../utils.js'; import { api, makeStreamCatcher, post, signup, uploadFile } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';

View File

@@ -6,10 +6,11 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, beforeAll, test, expect } from 'vitest';
// node-fetch only supports it's own Blob yet // node-fetch only supports it's own Blob yet
// https://github.com/node-fetch/node-fetch/pull/1664 // https://github.com/node-fetch/node-fetch/pull/1664
import { Blob } from 'node-fetch'; import { Blob } from 'node-fetch';
import { api, castAsError, initTestDb, post, signup, simpleGet, uploadFile } from '../utils.js'; import { api, castAsError, initTestDb, post, role, signup, simpleGet, uploadFile } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
import { MiUser } from '@/models/_.js'; import { MiUser } from '@/models/_.js';
@@ -581,6 +582,30 @@ describe('Endpoints', () => {
}); });
describe('drive/files/create', () => { describe('drive/files/create', () => {
const assignRole = async (userId: string, policies: Record<string, unknown>) => {
const createdRole = await role(alice, {}, policies);
const assign = await api('admin/roles/assign', {
userId,
roleId: createdRole.id,
}, alice);
assert.strictEqual(assign.status, 204);
return createdRole;
};
const cleanupRole = async (userId: string, roleId: string) => {
await api('admin/roles/unassign', {
userId,
roleId,
}, alice);
await api('admin/roles/delete', {
roleId,
}, alice);
};
test('ファイルを作成できる', async () => { test('ファイルを作成できる', async () => {
const res = await uploadFile(alice); const res = await uploadFile(alice);
@@ -659,6 +684,104 @@ describe('Endpoints', () => {
assert.strictEqual(webpublicType, 'image/webp'); assert.strictEqual(webpublicType, 'image/webp');
}); });
} }
test('uploadableFileTypes が */* なら任意のファイルをアップロードできる', async () => {
const createdRole = await assignRole(bob.id, {
uploadableFileTypes: {
useDefault: false,
priority: 1,
value: ['*/*'],
},
});
try {
const res = await uploadFile(bob, {
blob: new Blob([new Uint8Array(10)]),
});
assert.strictEqual(res.status, 200);
} finally {
await cleanupRole(bob.id, createdRole.id);
}
});
test('uploadableFileTypes に含まれない MIME type は拒否される', async () => {
const createdRole = await assignRole(bob.id, {
uploadableFileTypes: {
useDefault: false,
priority: 1,
value: ['image/png'],
},
});
try {
const res = await uploadFile(bob, { path: '192.jpg' });
assert.strictEqual(res.status, 400);
assert.ok(res.body);
assert.strictEqual(castAsError(res.body).error.code, 'UNALLOWED_FILE_TYPE');
} finally {
await cleanupRole(bob.id, createdRole.id);
}
});
test('maxFileSizeMb 制限付きロールでも制限内ならアップロードできる', async () => {
const allowAllTypesRole = await assignRole(bob.id, {
uploadableFileTypes: {
useDefault: false,
priority: 1,
value: ['*/*'],
},
});
const tinyAttachmentRole = await assignRole(bob.id, {
maxFileSizeMb: {
useDefault: false,
priority: 1,
value: 10 / 1024 / 1024, // 10バイト
},
});
try {
const res = await uploadFile(bob, {
blob: new Blob([new Uint8Array(10)]),
});
assert.strictEqual(res.status, 200);
} finally {
await cleanupRole(bob.id, tinyAttachmentRole.id);
await cleanupRole(bob.id, allowAllTypesRole.id);
}
});
test('maxFileSizeMb 制限を超えると 413 になる', async () => {
const allowAllTypesRole = await assignRole(bob.id, {
uploadableFileTypes: {
useDefault: false,
priority: 1,
value: ['*/*'],
},
});
const tinyAttachmentRole = await assignRole(bob.id, {
maxFileSizeMb: {
useDefault: false,
priority: 1,
value: 10 / 1024 / 1024, // 10バイト
},
});
try {
const res = await uploadFile(bob, {
blob: new Blob([new Uint8Array(11)]),
});
assert.strictEqual(res.status, 413);
assert.ok(res.body);
assert.strictEqual(castAsError(res.body).error.code, 'MAX_FILE_SIZE_EXCEEDED');
} finally {
await cleanupRole(bob.id, tinyAttachmentRole.id);
await cleanupRole(bob.id, allowAllTypesRole.id);
}
});
}); });
describe('drive/files/update', () => { describe('drive/files/update', () => {

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { afterAll, beforeAll, beforeEach, describe, test } from 'vitest';
import { api, port, post, signup, startJobQueue } from '../utils.js'; import { api, port, post, signup, startJobQueue } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common'; import type { INestApplicationContext } from '@nestjs/common';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { beforeAll, beforeEach, describe, test } from 'vitest';
import { api, channel, clip, galleryPost, page, play, post, signup, simpleGet, uploadFile } from '../utils.js'; import { api, channel, clip, galleryPost, page, play, post, signup, simpleGet, uploadFile } from '../utils.js';
import type { SimpleGetResponse } from '../utils.js'; import type { SimpleGetResponse } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';

View File

@@ -5,6 +5,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import { beforeAll, describe, test, expect } from 'vitest';
import { validateContentTypeSetAsActivityPub, validateContentTypeSetAsJsonLD } from '@/core/activitypub/misc/validator.js'; import { validateContentTypeSetAsActivityPub, validateContentTypeSetAsJsonLD } from '@/core/activitypub/misc/validator.js';
import { signup, uploadFile, relativeFetch } from '../utils.js'; import { signup, uploadFile, relativeFetch } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, beforeAll, test } from 'vitest';
import { api, signup, simpleGet } from '../utils.js'; import { api, signup, simpleGet } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';

View File

@@ -9,6 +9,7 @@ process.env.NODE_ENV = 'test';
import { setTimeout } from 'node:timers/promises'; import { setTimeout } from 'node:timers/promises';
import * as assert from 'assert'; import * as assert from 'assert';
import { afterAll, beforeAll, afterEach, describe, test } from 'vitest';
import { loadConfig } from '@/config.js'; import { loadConfig } from '@/config.js';
import { MiRepository, MiUser, UsersRepository, miRepository } from '@/models/_.js'; import { MiRepository, MiUser, UsersRepository, miRepository } from '@/models/_.js';
import { secureRndstr } from '@/misc/secure-rndstr.js'; import { secureRndstr } from '@/misc/secure-rndstr.js';

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { beforeAll, describe, test } from 'vitest';
import { api, post, react, signup, waitFire } from '../utils.js'; import { api, post, react, signup, waitFire } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, test } from 'vitest';
import { relativeFetch } from '../utils.js'; import { relativeFetch } from '../utils.js';
describe('nodeinfo', () => { describe('nodeinfo', () => {

View File

@@ -8,6 +8,7 @@ import type { Repository } from "typeorm";
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, beforeAll, afterAll, test } from 'vitest';
import { MiNote } from '@/models/Note.js'; import { MiNote } from '@/models/Note.js';
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
import { api, castAsError, initTestDb, post, role, signup, uploadFile, uploadUrl } from '../utils.js'; import { api, castAsError, initTestDb, post, role, signup, uploadFile, uploadUrl } from '../utils.js';

View File

@@ -11,6 +11,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { afterAll, beforeAll, beforeEach, describe, test } from 'vitest';
import { import {
AuthorizationCode, AuthorizationCode,
type AuthorizationTokenConfig, type AuthorizationTokenConfig,

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { beforeAll, describe, test } from 'vitest';
import { setTimeout } from 'node:timers/promises'; import { setTimeout } from 'node:timers/promises';
import { api, post, signup, waitFire } from '../utils.js'; import { api, post, signup, waitFire } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, beforeAll, test } from 'vitest';
import { ReversiMatchResponse } from 'misskey-js/entities.js'; import { ReversiMatchResponse } from 'misskey-js/entities.js';
import { api, signup } from '../utils.js'; import { api, signup } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, beforeAll, test } from 'vitest';
import { WebSocket } from 'ws'; import { WebSocket } from 'ws';
import { MiFollowing } from '@/models/Following.js'; import { MiFollowing } from '@/models/Following.js';
import { api, createAppToken, initTestDb, port, post, signup, waitFire } from '../utils.js'; import { api, createAppToken, initTestDb, port, post, signup, waitFire } from '../utils.js';

View File

@@ -4,7 +4,14 @@
*/ */
import { entities } from 'misskey-js'; import { entities } from 'misskey-js';
import { beforeEach, describe, test } from '@jest/globals'; import {
beforeEach,
beforeAll,
afterAll,
describe,
expect,
test,
} from 'vitest';
import { import {
api, api,
captureWebhook, captureWebhook,

View File

@@ -5,7 +5,7 @@
import { setTimeout } from 'node:timers/promises'; import { setTimeout } from 'node:timers/promises';
import { entities } from 'misskey-js'; import { entities } from 'misskey-js';
import { beforeEach, describe, test } from '@jest/globals'; import { beforeEach, describe, test, beforeAll, afterAll, expect } from 'vitest';
import { import {
api, api,
captureWebhook, captureWebhook,

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { beforeAll, describe, test } from 'vitest';
import { api, connectStream, post, signup } from '../utils.js'; import { api, connectStream, post, signup } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';

View File

@@ -9,6 +9,7 @@
// pnpm jest -- e2e/timelines.ts // pnpm jest -- e2e/timelines.ts
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, beforeAll, test } from 'vitest';
import { setTimeout } from 'node:timers/promises'; import { setTimeout } from 'node:timers/promises';
import { entities } from 'misskey-js'; import { entities } from 'misskey-js';
import { Redis } from 'ioredis'; import { Redis } from 'ioredis';

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { beforeAll, describe, test } from 'vitest';
import { api, post, signup, uploadUrl } from '../utils.js'; import { api, post, signup, uploadUrl } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { beforeAll, beforeEach, describe, test } from 'vitest';
import { inspect } from 'node:util'; import { inspect } from 'node:util';
import { api, post, role, signup, successfulApiCall, uploadFile } from '../utils.js'; import { api, post, role, signup, successfulApiCall, uploadFile } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';

View File

@@ -6,6 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { beforeAll, describe, test } from 'vitest';
import { api, host, origin, relativeFetch, signup } from '../utils.js'; import { api, host, origin, relativeFetch, signup } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';

View File

@@ -9,7 +9,6 @@ export default [
languageOptions: { languageOptions: {
globals: { globals: {
...globals.node, ...globals.node,
...globals.jest,
}, },
parserOptions: { parserOptions: {
parser: tsParser, parser: tsParser,

View File

@@ -4,6 +4,7 @@
*/ */
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, test } from 'vitest';
import { query } from '../../src/misc/prelude/url.js'; import { query } from '../../src/misc/prelude/url.js';
describe('url', () => { describe('url', () => {

View File

@@ -3,10 +3,10 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { beforeAll } from 'vitest';
import { initTestDb, sendEnvResetRequest } from './utils.js'; import { initTestDb, sendEnvResetRequest } from './utils.js';
beforeAll(async () => { beforeAll(async () => {
await initTestDb(false); await initTestDb(false);
await sendEnvResetRequest(); await sendEnvResetRequest();
}); });

View File

@@ -3,8 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
module.exports = async () => { export default function setup() {
// DBはUTCっぽいので、テスト側も合わせておく // DBはUTCっぽいので、テスト側も合わせておく
process.env.TZ = 'UTC'; process.env.TZ = 'UTC';
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
}; }

View File

@@ -3,7 +3,7 @@
"allowJs": true, "allowJs": true,
"noEmitOnError": false, "noEmitOnError": false,
"noImplicitAny": true, "noImplicitAny": true,
"noImplicitReturns": true, "noImplicitReturns": false,
"noUnusedParameters": false, "noUnusedParameters": false,
"noUnusedLocals": false, "noUnusedLocals": false,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
@@ -35,7 +35,7 @@
"lib": [ "lib": [
"esnext" "esnext"
], ],
"types": ["jest", "node"] "types": ["node"]
}, },
"compileOnSave": false, "compileOnSave": false,
"include": [ "include": [

View File

@@ -3,7 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { describe, jest } from '@jest/globals'; import { describe, expect, beforeAll, afterAll, beforeEach, afterEach, test, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { randomString } from '../utils.js'; import { randomString } from '../utils.js';
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js'; import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
@@ -42,9 +43,9 @@ describe('AbuseReportNotificationService', () => {
let systemWebhooksRepository: SystemWebhooksRepository; let systemWebhooksRepository: SystemWebhooksRepository;
let abuseReportNotificationRecipientRepository: AbuseReportNotificationRecipientRepository; let abuseReportNotificationRecipientRepository: AbuseReportNotificationRecipientRepository;
let idService: IdService; let idService: IdService;
let roleService: jest.Mocked<RoleService>; let roleService: Mocked<RoleService>;
let emailService: jest.Mocked<EmailService>; let emailService: Mocked<EmailService>;
let webhookService: jest.Mocked<SystemWebhookService>; let webhookService: Mocked<SystemWebhookService>;
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@@ -107,10 +108,10 @@ describe('AbuseReportNotificationService', () => {
AbuseReportNotificationService, AbuseReportNotificationService,
IdService, IdService,
{ {
provide: RoleService, useFactory: () => ({ getModeratorIds: jest.fn() }), provide: RoleService, useFactory: () => ({ getModeratorIds: vi.fn() }),
}, },
{ {
provide: SystemWebhookService, useFactory: () => ({ enqueueSystemWebhook: jest.fn() }), provide: SystemWebhookService, useFactory: () => ({ enqueueSystemWebhook: vi.fn() }),
}, },
{ {
provide: UserEntityService, useFactory: () => ({ provide: UserEntityService, useFactory: () => ({
@@ -119,16 +120,16 @@ describe('AbuseReportNotificationService', () => {
}), }),
}, },
{ {
provide: EmailService, useFactory: () => ({ sendEmail: jest.fn() }), provide: EmailService, useFactory: () => ({ sendEmail: vi.fn() }),
}, },
{ {
provide: MetaService, useFactory: () => ({ fetch: jest.fn() }), provide: MetaService, useFactory: () => ({ fetch: vi.fn() }),
}, },
{ {
provide: ModerationLogService, useFactory: () => ({ log: () => Promise.resolve() }), provide: ModerationLogService, useFactory: () => ({ log: () => Promise.resolve() }),
}, },
{ {
provide: GlobalEventService, useFactory: () => ({ publishAdminStream: jest.fn() }), provide: GlobalEventService, useFactory: () => ({ publishAdminStream: vi.fn() }),
}, },
], ],
}) })
@@ -141,9 +142,9 @@ describe('AbuseReportNotificationService', () => {
service = app.get(AbuseReportNotificationService); service = app.get(AbuseReportNotificationService);
idService = app.get(IdService); idService = app.get(IdService);
roleService = app.get(RoleService) as jest.Mocked<RoleService>; roleService = app.get(RoleService) as Mocked<RoleService>;
emailService = app.get<EmailService>(EmailService) as jest.Mocked<EmailService>; emailService = app.get<EmailService>(EmailService) as Mocked<EmailService>;
webhookService = app.get<SystemWebhookService>(SystemWebhookService) as jest.Mocked<SystemWebhookService>; webhookService = app.get<SystemWebhookService>(SystemWebhookService) as Mocked<SystemWebhookService>;
app.enableShutdownHooks(); app.enableShutdownHooks();
}); });

View File

@@ -5,8 +5,9 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import { jest } from '@jest/globals'; import { describe, expect, beforeEach, afterEach, test, vi } from 'vitest';
import { ModuleMocker } from 'jest-mock'; import type { Mocked } from 'vitest';
import { mockDeep } from 'vitest-mock-extended';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { GlobalModule } from '@/GlobalModule.js'; import { GlobalModule } from '@/GlobalModule.js';
import { AnnouncementService } from '@/core/AnnouncementService.js'; import { AnnouncementService } from '@/core/AnnouncementService.js';
@@ -26,9 +27,6 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js';
import { secureRndstr } from '@/misc/secure-rndstr.js'; import { secureRndstr } from '@/misc/secure-rndstr.js';
import type { TestingModule } from '@nestjs/testing'; import type { TestingModule } from '@nestjs/testing';
import type { MockMetadata } from 'jest-mock';
const moduleMocker = new ModuleMocker(global);
describe('AnnouncementService', () => { describe('AnnouncementService', () => {
let app: TestingModule; let app: TestingModule;
@@ -36,8 +34,8 @@ describe('AnnouncementService', () => {
let usersRepository: UsersRepository; let usersRepository: UsersRepository;
let announcementsRepository: AnnouncementsRepository; let announcementsRepository: AnnouncementsRepository;
let announcementReadsRepository: AnnouncementReadsRepository; let announcementReadsRepository: AnnouncementReadsRepository;
let globalEventService: jest.Mocked<GlobalEventService>; let globalEventService: Mocked<GlobalEventService>;
let moderationLogService: jest.Mocked<ModerationLogService>; let moderationLogService: Mocked<ModerationLogService>;
function createUser(data: Partial<MiUser> = {}) { function createUser(data: Partial<MiUser> = {}) {
const un = secureRndstr(16); const un = secureRndstr(16);
@@ -76,17 +74,15 @@ describe('AnnouncementService', () => {
.useMocker((token) => { .useMocker((token) => {
if (token === GlobalEventService) { if (token === GlobalEventService) {
return { return {
publishMainStream: jest.fn(), publishMainStream: vi.fn(),
publishBroadcastStream: jest.fn(), publishBroadcastStream: vi.fn(),
}; };
} else if (token === ModerationLogService) { } else if (token === ModerationLogService) {
return { return {
log: jest.fn(), log: vi.fn(),
}; };
} else if (typeof token === 'function') { } else if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>; return mockDeep<typeof token>();
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
} }
}) })
.compile(); .compile();
@@ -97,8 +93,8 @@ describe('AnnouncementService', () => {
usersRepository = app.get<UsersRepository>(DI.usersRepository); usersRepository = app.get<UsersRepository>(DI.usersRepository);
announcementsRepository = app.get<AnnouncementsRepository>(DI.announcementsRepository); announcementsRepository = app.get<AnnouncementsRepository>(DI.announcementsRepository);
announcementReadsRepository = app.get<AnnouncementReadsRepository>(DI.announcementReadsRepository); announcementReadsRepository = app.get<AnnouncementReadsRepository>(DI.announcementReadsRepository);
globalEventService = app.get<GlobalEventService>(GlobalEventService) as jest.Mocked<GlobalEventService>; globalEventService = app.get<GlobalEventService>(GlobalEventService) as Mocked<GlobalEventService>;
moderationLogService = app.get<ModerationLogService>(ModerationLogService) as jest.Mocked<ModerationLogService>; moderationLogService = app.get<ModerationLogService>(ModerationLogService) as Mocked<ModerationLogService>;
}); });
afterEach(async () => { afterEach(async () => {
@@ -203,7 +199,7 @@ describe('AnnouncementService', () => {
}); });
}); });
describe('read', () => { describe.todo('read', () => {
// TODO // TODO
}); });
}); });

View File

@@ -4,6 +4,7 @@
*/ */
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, test, beforeAll } from 'vitest';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { CoreModule } from '@/core/CoreModule.js'; import { CoreModule } from '@/core/CoreModule.js';

View File

@@ -3,7 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { afterAll, beforeAll, beforeEach, describe, expect, jest } from '@jest/globals'; import { afterAll, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { Response } from 'node-fetch'; import { Response } from 'node-fetch';
import { import {
@@ -22,8 +23,8 @@ import { LoggerService } from '@/core/LoggerService.js';
describe('CaptchaService', () => { describe('CaptchaService', () => {
let app: TestingModule; let app: TestingModule;
let service: CaptchaService; let service: CaptchaService;
let httpRequestService: jest.Mocked<HttpRequestService>; let httpRequestService: Mocked<HttpRequestService>;
let metaService: jest.Mocked<MetaService>; let metaService: Mocked<MetaService>;
beforeAll(async () => { beforeAll(async () => {
app = await Test.createTestingModule({ app = await Test.createTestingModule({
@@ -34,12 +35,12 @@ describe('CaptchaService', () => {
CaptchaService, CaptchaService,
LoggerService, LoggerService,
{ {
provide: HttpRequestService, useFactory: () => ({ send: jest.fn() }), provide: HttpRequestService, useFactory: () => ({ send: vi.fn() }),
}, },
{ {
provide: MetaService, useFactory: () => ({ provide: MetaService, useFactory: () => ({
fetch: jest.fn(), fetch: vi.fn(),
update: jest.fn(), update: vi.fn(),
}), }),
}, },
], ],
@@ -48,8 +49,8 @@ describe('CaptchaService', () => {
app.enableShutdownHooks(); app.enableShutdownHooks();
service = app.get(CaptchaService); service = app.get(CaptchaService);
httpRequestService = app.get(HttpRequestService) as jest.Mocked<HttpRequestService>; httpRequestService = app.get(HttpRequestService) as Mocked<HttpRequestService>;
metaService = app.get(MetaService) as jest.Mocked<MetaService>; metaService = app.get(MetaService) as Mocked<MetaService>;
}); });
beforeEach(() => { beforeEach(() => {

View File

@@ -5,7 +5,7 @@
/* eslint-disable */ /* eslint-disable */
import { afterEach, beforeEach, describe, expect } from '@jest/globals'; import { afterEach, beforeEach, describe, expect, beforeAll, afterAll, test } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { GlobalModule } from '@/GlobalModule.js'; import { GlobalModule } from '@/GlobalModule.js';
import { CoreModule } from '@/core/CoreModule.js'; import { CoreModule } from '@/core/CoreModule.js';

View File

@@ -5,7 +5,7 @@
/* eslint-disable */ /* eslint-disable */
import { afterEach, beforeEach, describe, expect } from '@jest/globals'; import { afterEach, beforeEach, describe, expect, beforeAll, afterAll, test } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { GlobalModule } from '@/GlobalModule.js'; import { GlobalModule } from '@/GlobalModule.js';
import { CoreModule } from '@/core/CoreModule.js'; import { CoreModule } from '@/core/CoreModule.js';

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { afterEach, beforeAll, describe, test } from '@jest/globals'; import { afterEach, beforeAll, describe, test, expect } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js';
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';

View File

@@ -5,6 +5,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import { afterAll, beforeAll, beforeEach, describe, test, expect } from 'vitest';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { import {
DeleteObjectCommand, DeleteObjectCommand,

View File

@@ -5,7 +5,8 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import { jest } from '@jest/globals'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { Redis } from 'ioredis'; import { Redis } from 'ioredis';
import type { TestingModule } from '@nestjs/testing'; import type { TestingModule } from '@nestjs/testing';
@@ -18,22 +19,32 @@ import { UtilityService } from '@/core/UtilityService.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
function mockRedis() { function createMockRedis() {
const hash = {} as any; const store = new Map<string, string>();
const set = jest.fn((key: string, value) => {
const ret = hash[key]; const del = vi.fn((key: string) => {
hash[key] = value; const existed = store.delete(key);
return ret; return Promise.resolve(existed ? 1 : 0);
}); });
return set;
const set = vi.fn((key: string, value: string, ...args: any[]) => {
const prev = store.get(key) ?? null;
store.set(key, value);
// ioredis: SET key value ... GET => returns old value or null
const hasGet = args.some(a => typeof a === 'string' && a.toUpperCase() === 'GET');
return Promise.resolve(hasGet ? prev : 'OK');
});
return { set, del };
} }
describe('FetchInstanceMetadataService', () => { describe('FetchInstanceMetadataService', () => {
let app: TestingModule; let app: TestingModule;
let fetchInstanceMetadataService: jest.Mocked<FetchInstanceMetadataService>; let fetchInstanceMetadataService: Mocked<FetchInstanceMetadataService>;
let federatedInstanceService: jest.Mocked<FederatedInstanceService>; let federatedInstanceService: Mocked<FederatedInstanceService>;
let httpRequestService: jest.Mocked<HttpRequestService>; let httpRequestService: Mocked<HttpRequestService>;
let redisClient: jest.Mocked<Redis>; let redisClient: Mocked<Redis>;
beforeEach(async () => { beforeEach(async () => {
app = await Test app = await Test
@@ -50,11 +61,11 @@ describe('FetchInstanceMetadataService', () => {
}) })
.useMocker((token) => { .useMocker((token) => {
if (token === HttpRequestService) { if (token === HttpRequestService) {
return { getJson: jest.fn(), getHtml: jest.fn(), send: jest.fn() }; return { getJson: vi.fn(), getHtml: vi.fn(), send: vi.fn() };
} else if (token === FederatedInstanceService) { } else if (token === FederatedInstanceService) {
return { fetchOrRegister: jest.fn() }; return { fetchOrRegister: vi.fn() };
} else if (token === DI.redis) { } else if (token === DI.redis) {
return mockRedis; return createMockRedis();
} }
return null; return null;
}) })
@@ -62,23 +73,24 @@ describe('FetchInstanceMetadataService', () => {
app.enableShutdownHooks(); app.enableShutdownHooks();
fetchInstanceMetadataService = app.get<FetchInstanceMetadataService>(FetchInstanceMetadataService) as jest.Mocked<FetchInstanceMetadataService>; fetchInstanceMetadataService = app.get<FetchInstanceMetadataService>(FetchInstanceMetadataService) as Mocked<FetchInstanceMetadataService>;
federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService) as jest.Mocked<FederatedInstanceService>; federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService) as Mocked<FederatedInstanceService>;
redisClient = app.get<Redis>(DI.redis) as jest.Mocked<Redis>; redisClient = app.get<Redis>(DI.redis) as Mocked<Redis>;
httpRequestService = app.get<HttpRequestService>(HttpRequestService) as jest.Mocked<HttpRequestService>; httpRequestService = app.get<HttpRequestService>(HttpRequestService) as Mocked<HttpRequestService>;
}); });
afterEach(async () => { afterEach(async () => {
await app.close(); await app.close();
vi.resetAllMocks();
vi.clearAllMocks();
}); });
test('Lock and update', async () => { test('Lock and update', async () => {
redisClient.set = mockRedis();
const now = Date.now(); const now = Date.now();
federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => { return now - 10 * 1000 * 60 * 60 * 24; } } } as any); federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => { return now - 10 * 1000 * 60 * 60 * 24; } } } as any);
httpRequestService.getJson.mockImplementation(() => { throw Error(); }); httpRequestService.getJson.mockImplementation(() => { throw Error(); });
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock'); const tryLockSpy = vi.spyOn(fetchInstanceMetadataService, 'tryLock');
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock'); const unlockSpy = vi.spyOn(fetchInstanceMetadataService, 'unlock');
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any); await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
expect(tryLockSpy).toHaveBeenCalledTimes(1); expect(tryLockSpy).toHaveBeenCalledTimes(1);
@@ -88,12 +100,11 @@ describe('FetchInstanceMetadataService', () => {
}); });
test('Lock and don\'t update', async () => { test('Lock and don\'t update', async () => {
redisClient.set = mockRedis();
const now = Date.now(); const now = Date.now();
federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => now } } as any); federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => now } } as any);
httpRequestService.getJson.mockImplementation(() => { throw Error(); }); httpRequestService.getJson.mockImplementation(() => { throw Error(); });
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock'); const tryLockSpy = vi.spyOn(fetchInstanceMetadataService, 'tryLock');
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock'); const unlockSpy = vi.spyOn(fetchInstanceMetadataService, 'unlock');
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any); await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
expect(tryLockSpy).toHaveBeenCalledTimes(1); expect(tryLockSpy).toHaveBeenCalledTimes(1);
@@ -103,13 +114,12 @@ describe('FetchInstanceMetadataService', () => {
}); });
test('Do nothing when lock not acquired', async () => { test('Do nothing when lock not acquired', async () => {
redisClient.set = mockRedis();
const now = Date.now(); const now = Date.now();
federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any); federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
httpRequestService.getJson.mockImplementation(() => { throw Error(); }); httpRequestService.getJson.mockImplementation(() => { throw Error(); });
await fetchInstanceMetadataService.tryLock('example.com'); await fetchInstanceMetadataService.tryLock('example.com');
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock'); const tryLockSpy = vi.spyOn(fetchInstanceMetadataService, 'tryLock');
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock'); const unlockSpy = vi.spyOn(fetchInstanceMetadataService, 'unlock');
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any); await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
expect(tryLockSpy).toHaveBeenCalledTimes(1); expect(tryLockSpy).toHaveBeenCalledTimes(1);
@@ -119,13 +129,12 @@ describe('FetchInstanceMetadataService', () => {
}); });
test('Do when lock not acquired but forced', async () => { test('Do when lock not acquired but forced', async () => {
redisClient.set = mockRedis();
const now = Date.now(); const now = Date.now();
federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any); federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
httpRequestService.getJson.mockImplementation(() => { throw Error(); }); httpRequestService.getJson.mockImplementation(() => { throw Error(); });
await fetchInstanceMetadataService.tryLock('example.com'); await fetchInstanceMetadataService.tryLock('example.com');
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock'); const tryLockSpy = vi.spyOn(fetchInstanceMetadataService, 'tryLock');
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock'); const unlockSpy = vi.spyOn(fetchInstanceMetadataService, 'unlock');
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any, true); await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any, true);
expect(tryLockSpy).toHaveBeenCalledTimes(0); expect(tryLockSpy).toHaveBeenCalledTimes(0);

View File

@@ -8,23 +8,20 @@ process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path'; import { dirname } from 'node:path';
import { ModuleMocker } from 'jest-mock';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { afterAll, beforeAll, describe, test } from '@jest/globals'; import { afterAll, beforeAll, describe, test } from 'vitest';
import { mockDeep } from 'vitest-mock-extended';
import { GlobalModule } from '@/GlobalModule.js'; import { GlobalModule } from '@/GlobalModule.js';
import { FileInfo, FileInfoService } from '@/core/FileInfoService.js'; import { FileInfo, FileInfoService } from '@/core/FileInfoService.js';
//import { DI } from '@/di-symbols.js'; //import { DI } from '@/di-symbols.js';
import { AiService } from '@/core/AiService.js'; import { AiService } from '@/core/AiService.js';
import { LoggerService } from '@/core/LoggerService.js'; import { LoggerService } from '@/core/LoggerService.js';
import type { TestingModule } from '@nestjs/testing'; import type { TestingModule } from '@nestjs/testing';
import type { MockMetadata } from 'jest-mock';
const _filename = fileURLToPath(import.meta.url); const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename); const _dirname = dirname(_filename);
const resources = `${_dirname}/../resources`; const resources = `${_dirname}/../resources`;
const moduleMocker = new ModuleMocker(global);
describe('FileInfoService', () => { describe('FileInfoService', () => {
let app: TestingModule; let app: TestingModule;
let fileInfoService: FileInfoService; let fileInfoService: FileInfoService;
@@ -54,9 +51,7 @@ describe('FileInfoService', () => {
// return { }; // return { };
//} //}
if (typeof token === 'function') { if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>; return mockDeep<typeof token>();
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
} }
}) })
.compile(); .compile();

View File

@@ -5,6 +5,7 @@
*/ */
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { afterAll, afterEach, beforeEach, describe, expect, test } from 'vitest';
import { FlashService } from '@/core/FlashService.js'; import { FlashService } from '@/core/FlashService.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { FlashLikesRepository, FlashsRepository, MiFlash, MiUser, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import { FlashLikesRepository, FlashsRepository, MiFlash, MiUser, UserProfilesRepository, UsersRepository } from '@/models/_.js';

View File

@@ -5,7 +5,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import { jest } from '@jest/globals'; import { afterAll, beforeAll, describe, test, expect, vi } from 'vitest';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { GlobalModule } from '@/GlobalModule.js'; import { GlobalModule } from '@/GlobalModule.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
@@ -40,7 +40,7 @@ describe('MetaService', () => {
test('fetch (cache)', async () => { test('fetch (cache)', async () => {
const db = app.get<DataSource>(DI.db); const db = app.get<DataSource>(DI.db);
const spy = jest.spyOn(db, 'transaction'); const spy = vi.spyOn(db, 'transaction');
const result = await metaService.fetch(); const result = await metaService.fetch();
@@ -50,7 +50,7 @@ describe('MetaService', () => {
test('fetch (force)', async () => { test('fetch (force)', async () => {
const db = app.get<DataSource>(DI.db); const db = app.get<DataSource>(DI.db);
const spy = jest.spyOn(db, 'transaction'); const spy = vi.spyOn(db, 'transaction');
const result = await metaService.fetch(true); const result = await metaService.fetch(true);

View File

@@ -5,6 +5,7 @@
import * as assert from 'assert'; import * as assert from 'assert';
import * as mfm from 'mfm-js'; import * as mfm from 'mfm-js';
import { beforeAll, describe, test } from 'vitest';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { CoreModule } from '@/core/CoreModule.js'; import { CoreModule } from '@/core/CoreModule.js';

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { beforeAll, describe, test, expect } from 'vitest';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { CoreModule } from '@/core/CoreModule.js'; import { CoreModule } from '@/core/CoreModule.js';

View File

@@ -4,6 +4,7 @@
*/ */
import * as assert from 'assert'; import * as assert from 'assert';
import { beforeAll, describe, test } from 'vitest';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { CoreModule } from '@/core/CoreModule.js'; import { CoreModule } from '@/core/CoreModule.js';

View File

@@ -5,11 +5,11 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import { jest } from '@jest/globals'; import { afterAll, beforeAll, describe, test, expect, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { ModuleMocker } from 'jest-mock'; import { mockDeep } from 'vitest-mock-extended';
import type { TestingModule } from '@nestjs/testing'; import type { TestingModule } from '@nestjs/testing';
import type { MockMetadata } from 'jest-mock';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
@@ -19,12 +19,10 @@ import { SystemAccountService } from '@/core/SystemAccountService.js';
import { GlobalModule } from '@/GlobalModule.js'; import { GlobalModule } from '@/GlobalModule.js';
import { UtilityService } from '@/core/UtilityService.js'; import { UtilityService } from '@/core/UtilityService.js';
const moduleMocker = new ModuleMocker(global);
describe('RelayService', () => { describe('RelayService', () => {
let app: TestingModule; let app: TestingModule;
let relayService: RelayService; let relayService: RelayService;
let queueService: jest.Mocked<QueueService>; let queueService: Mocked<QueueService>;
beforeAll(async () => { beforeAll(async () => {
app = await Test.createTestingModule({ app = await Test.createTestingModule({
@@ -42,12 +40,10 @@ describe('RelayService', () => {
}) })
.useMocker((token) => { .useMocker((token) => {
if (token === QueueService) { if (token === QueueService) {
return { deliver: jest.fn() }; return { deliver: vi.fn() };
} }
if (typeof token === 'function') { if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>; return mockDeep<typeof token>();
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
} }
}) })
.compile(); .compile();
@@ -55,7 +51,7 @@ describe('RelayService', () => {
app.enableShutdownHooks(); app.enableShutdownHooks();
relayService = app.get<RelayService>(RelayService); relayService = app.get<RelayService>(RelayService);
queueService = app.get<QueueService>(QueueService) as jest.Mocked<QueueService>; queueService = app.get<QueueService>(QueueService) as Mocked<QueueService>;
}); });
afterAll(async () => { afterAll(async () => {

View File

@@ -6,12 +6,12 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import { setTimeout } from 'node:timers/promises'; import { setTimeout } from 'node:timers/promises';
import { describe, jest } from '@jest/globals'; import { describe, beforeEach, afterEach, test, expect, vi } from 'vitest';
import { ModuleMocker } from 'jest-mock'; import type { Mocked } from 'vitest';
import { mockDeep } from 'vitest-mock-extended';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import * as lolex from '@sinonjs/fake-timers'; import * as lolex from '@sinonjs/fake-timers';
import type { TestingModule } from '@nestjs/testing'; import type { TestingModule } from '@nestjs/testing';
import type { MockMetadata } from 'jest-mock';
import { GlobalModule } from '@/GlobalModule.js'; import { GlobalModule } from '@/GlobalModule.js';
import { RoleService } from '@/core/RoleService.js'; import { RoleService } from '@/core/RoleService.js';
import { import {
@@ -34,16 +34,14 @@ import { NotificationService } from '@/core/NotificationService.js';
import { RoleCondFormulaValue } from '@/models/Role.js'; import { RoleCondFormulaValue } from '@/models/Role.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
const moduleMocker = new ModuleMocker(global);
describe('RoleService', () => { describe('RoleService', () => {
let app: TestingModule; let app: TestingModule;
let roleService: RoleService; let roleService: RoleService;
let usersRepository: UsersRepository; let usersRepository: UsersRepository;
let rolesRepository: RolesRepository; let rolesRepository: RolesRepository;
let roleAssignmentsRepository: RoleAssignmentsRepository; let roleAssignmentsRepository: RoleAssignmentsRepository;
let meta: jest.Mocked<MiMeta>; let meta: Mocked<MiMeta>;
let notificationService: jest.Mocked<NotificationService>; let notificationService: Mocked<NotificationService>;
let clock: lolex.Clock; let clock: lolex.Clock;
async function createUser(data: Partial<MiUser> = {}) { async function createUser(data: Partial<MiUser> = {}) {
@@ -123,7 +121,7 @@ describe('RoleService', () => {
{ {
provide: NotificationService, provide: NotificationService,
useFactory: () => ({ useFactory: () => ({
createNotification: jest.fn(), createNotification: vi.fn(),
}), }),
}, },
{ {
@@ -134,12 +132,10 @@ describe('RoleService', () => {
}) })
.useMocker((token) => { .useMocker((token) => {
if (token === MetaService) { if (token === MetaService) {
return { fetch: jest.fn() }; return { fetch: vi.fn() };
} }
if (typeof token === 'function') { if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>; return mockDeep<typeof token>();
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
} }
}) })
.compile(); .compile();
@@ -151,8 +147,8 @@ describe('RoleService', () => {
rolesRepository = app.get<RolesRepository>(DI.rolesRepository); rolesRepository = app.get<RolesRepository>(DI.rolesRepository);
roleAssignmentsRepository = app.get<RoleAssignmentsRepository>(DI.roleAssignmentsRepository); roleAssignmentsRepository = app.get<RoleAssignmentsRepository>(DI.roleAssignmentsRepository);
meta = app.get<MiMeta>(DI.meta) as jest.Mocked<MiMeta>; meta = app.get<MiMeta>(DI.meta) as Mocked<MiMeta>;
notificationService = app.get<NotificationService>(NotificationService) as jest.Mocked<NotificationService>; notificationService = app.get<NotificationService>(NotificationService) as Mocked<NotificationService>;
await roleService.onModuleInit(); await roleService.onModuleInit();
}); });

View File

@@ -5,6 +5,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import { afterAll, beforeAll, beforeEach, describe, test, expect } from 'vitest';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { import {
CompleteMultipartUploadCommand, CompleteMultipartUploadCommand,

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { afterAll, afterEach, beforeAll, describe, expect, test } from '@jest/globals'; import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import type { Index, Meilisearch } from 'meilisearch'; import type { Index, Meilisearch } from 'meilisearch';
import { type Config, loadConfig } from '@/config.js'; import { type Config, loadConfig } from '@/config.js';

View File

@@ -4,12 +4,12 @@
*/ */
import { IncomingHttpHeaders } from 'node:http'; import { IncomingHttpHeaders } from 'node:http';
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, jest, test } from '@jest/globals'; import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { mockDeep } from 'vitest-mock-extended';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { FastifyReply, FastifyRequest } from 'fastify'; import { FastifyReply, FastifyRequest } from 'fastify';
import { AuthenticationResponseJSON } from '@simplewebauthn/types'; import { AuthenticationResponseJSON } from '@simplewebauthn/types';
import { HttpHeader } from 'fastify/types/utils.js'; import { HttpHeader } from 'fastify/types/utils.js';
import { MockMetadata, ModuleMocker } from 'jest-mock';
import { MiUser } from '@/models/User.js'; import { MiUser } from '@/models/User.js';
import { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
@@ -22,8 +22,6 @@ import { WebAuthnService } from '@/core/WebAuthnService.js';
import { SigninService } from '@/server/api/SigninService.js'; import { SigninService } from '@/server/api/SigninService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js'; import { IdentifiableError } from '@/misc/identifiable-error.js';
const moduleMocker = new ModuleMocker(global);
class FakeLimiter { class FakeLimiter {
public async limit() { public async limit() {
return; return;
@@ -95,9 +93,7 @@ describe('SigninWithPasskeyApiService', () => {
], ],
}).useMocker((token) => { }).useMocker((token) => {
if (typeof token === 'function') { if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>; return mockDeep<typeof token>();
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
} }
}).compile(); }).compile();
passkeyApiService = app.get<SigninWithPasskeyApiService>(SigninWithPasskeyApiService); passkeyApiService = app.get<SigninWithPasskeyApiService>(SigninWithPasskeyApiService);
@@ -112,7 +108,7 @@ describe('SigninWithPasskeyApiService', () => {
FakeWebauthnVerify = async () => { FakeWebauthnVerify = async () => {
return uid; return uid;
}; };
jest.spyOn(webAuthnService, 'verifySignInWithPasskeyAuthentication').mockImplementation(FakeWebauthnVerify); vi.spyOn(webAuthnService, 'verifySignInWithPasskeyAuthentication').mockImplementation(FakeWebauthnVerify);
const dummyUser = { const dummyUser = {
id: uid, username: uid, usernameLower: uid.toLowerCase(), uri: null, host: null, id: uid, username: uid, usernameLower: uid.toLowerCase(), uri: null, host: null,
@@ -159,7 +155,7 @@ describe('SigninWithPasskeyApiService', () => {
it('Should return 403 When Challenge Verify fail', async () => { it('Should return 403 When Challenge Verify fail', async () => {
const req = new DummyFastifyRequest({ context: 'misskey-1234', credential: { dummy: [] } }) as ApiFastifyRequestType; const req = new DummyFastifyRequest({ context: 'misskey-1234', credential: { dummy: [] } }) as ApiFastifyRequestType;
const res = new DummyFastifyReply() as FastifyReply; const res = new DummyFastifyReply() as FastifyReply;
jest.spyOn(webAuthnService, 'verifySignInWithPasskeyAuthentication') vi.spyOn(webAuthnService, 'verifySignInWithPasskeyAuthentication')
.mockImplementation(async () => { .mockImplementation(async () => {
throw new IdentifiableError('THIS_ERROR_CODE_SHOULD_BE_FORWARDED'); throw new IdentifiableError('THIS_ERROR_CODE_SHOULD_BE_FORWARDED');
}); });

View File

@@ -5,7 +5,8 @@
*/ */
import { setTimeout } from 'node:timers/promises'; import { setTimeout } from 'node:timers/promises';
import { afterEach, beforeEach, describe, expect, jest } from '@jest/globals'; import { afterEach, beforeEach, afterAll, beforeAll, describe, test, expect, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { randomString } from '../utils.js'; import { randomString } from '../utils.js';
import { MiUser } from '@/models/User.js'; import { MiUser } from '@/models/User.js';
@@ -29,7 +30,7 @@ describe('SystemWebhookService', () => {
let usersRepository: UsersRepository; let usersRepository: UsersRepository;
let systemWebhooksRepository: SystemWebhooksRepository; let systemWebhooksRepository: SystemWebhooksRepository;
let idService: IdService; let idService: IdService;
let queueService: jest.Mocked<QueueService>; let queueService: Mocked<QueueService>;
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@@ -73,7 +74,7 @@ describe('SystemWebhookService', () => {
LoggerService, LoggerService,
GlobalEventService, GlobalEventService,
{ {
provide: QueueService, useFactory: () => ({ systemWebhookDeliver: jest.fn() }), provide: QueueService, useFactory: () => ({ systemWebhookDeliver: vi.fn() }),
}, },
{ {
provide: ModerationLogService, useFactory: () => ({ log: () => Promise.resolve() }), provide: ModerationLogService, useFactory: () => ({ log: () => Promise.resolve() }),
@@ -87,7 +88,7 @@ describe('SystemWebhookService', () => {
service = app.get(SystemWebhookService); service = app.get(SystemWebhookService);
idService = app.get(IdService); idService = app.get(IdService);
queueService = app.get(QueueService) as jest.Mocked<QueueService>; queueService = app.get(QueueService) as Mocked<QueueService>;
app.enableShutdownHooks(); app.enableShutdownHooks();
} }

View File

@@ -4,7 +4,7 @@
*/ */
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { describe, jest, test } from '@jest/globals'; import { describe, beforeEach, beforeAll, afterEach, afterAll, vi, test, expect } from 'vitest';
import { In } from 'typeorm'; import { In } from 'typeorm';
import { UserSearchService } from '@/core/UserSearchService.js'; import { UserSearchService } from '@/core/UserSearchService.js';
import { FollowingsRepository, MiUser, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import { FollowingsRepository, MiUser, UserProfilesRepository, UsersRepository } from '@/models/_.js';
@@ -92,7 +92,7 @@ describe('UserSearchService', () => {
providers: [ providers: [
UserSearchService, UserSearchService,
{ {
provide: UserEntityService, useFactory: jest.fn(() => ({ provide: UserEntityService, useFactory: vi.fn(() => ({
// とりあえずIDが返れば確認が出来るので // とりあえずIDが返れば確認が出来るので
packMany: (value: any) => value, packMany: (value: any) => value,
})), })),

View File

@@ -3,7 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { afterEach, beforeEach, describe, expect, jest } from '@jest/globals'; import { afterEach, beforeEach, describe, expect, test, beforeAll, afterAll, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { randomString } from '../utils.js'; import { randomString } from '../utils.js';
import { MiUser } from '@/models/User.js'; import { MiUser } from '@/models/User.js';
@@ -25,7 +26,7 @@ describe('UserWebhookService', () => {
let usersRepository: UsersRepository; let usersRepository: UsersRepository;
let userWebhooksRepository: WebhooksRepository; let userWebhooksRepository: WebhooksRepository;
let idService: IdService; let idService: IdService;
let queueService: jest.Mocked<QueueService>; let queueService: Mocked<QueueService>;
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@@ -70,7 +71,7 @@ describe('UserWebhookService', () => {
LoggerService, LoggerService,
GlobalEventService, GlobalEventService,
{ {
provide: QueueService, useFactory: () => ({ userWebhookDeliver: jest.fn() }), provide: QueueService, useFactory: () => ({ userWebhookDeliver: vi.fn() }),
}, },
], ],
}) })
@@ -81,7 +82,7 @@ describe('UserWebhookService', () => {
service = app.get(UserWebhookService); service = app.get(UserWebhookService);
idService = app.get(IdService); idService = app.get(IdService);
queueService = app.get(QueueService) as jest.Mocked<QueueService>; queueService = app.get(QueueService) as Mocked<QueueService>;
app.enableShutdownHooks(); app.enableShutdownHooks();
} }

View File

@@ -5,7 +5,8 @@
*/ */
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { beforeAll, describe, jest } from '@jest/globals'; import { beforeAll, afterAll, beforeEach, afterEach, test, expect, describe, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { WebhookTestService } from '@/core/WebhookTestService.js'; import { WebhookTestService } from '@/core/WebhookTestService.js';
import { UserWebhookPayload, UserWebhookService } from '@/core/UserWebhookService.js'; import { UserWebhookPayload, UserWebhookService } from '@/core/UserWebhookService.js';
import { SystemWebhookService } from '@/core/SystemWebhookService.js'; import { SystemWebhookService } from '@/core/SystemWebhookService.js';
@@ -24,9 +25,9 @@ describe('WebhookTestService', () => {
let usersRepository: UsersRepository; let usersRepository: UsersRepository;
let userProfilesRepository: UserProfilesRepository; let userProfilesRepository: UserProfilesRepository;
let queueService: jest.Mocked<QueueService>; let queueService: Mocked<QueueService>;
let userWebhookService: jest.Mocked<UserWebhookService>; let userWebhookService: Mocked<UserWebhookService>;
let systemWebhookService: jest.Mocked<SystemWebhookService>; let systemWebhookService: Mocked<SystemWebhookService>;
let idService: IdService; let idService: IdService;
let root: MiUser; let root: MiUser;
@@ -59,23 +60,23 @@ describe('WebhookTestService', () => {
IdService, IdService,
{ {
provide: CustomEmojiService, useFactory: () => ({ provide: CustomEmojiService, useFactory: () => ({
populateEmojis: jest.fn(), populateEmojis: vi.fn(),
}), }),
}, },
{ {
provide: QueueService, useFactory: () => ({ provide: QueueService, useFactory: () => ({
systemWebhookDeliver: jest.fn(), systemWebhookDeliver: vi.fn(),
userWebhookDeliver: jest.fn(), userWebhookDeliver: vi.fn(),
}), }),
}, },
{ {
provide: UserWebhookService, useFactory: () => ({ provide: UserWebhookService, useFactory: () => ({
fetchWebhooks: jest.fn(), fetchWebhooks: vi.fn(),
}), }),
}, },
{ {
provide: SystemWebhookService, useFactory: () => ({ provide: SystemWebhookService, useFactory: () => ({
fetchSystemWebhooks: jest.fn(), fetchSystemWebhooks: vi.fn(),
}), }),
}, },
], ],
@@ -86,9 +87,9 @@ describe('WebhookTestService', () => {
service = app.get(WebhookTestService); service = app.get(WebhookTestService);
idService = app.get(IdService); idService = app.get(IdService);
queueService = app.get(QueueService) as jest.Mocked<QueueService>; queueService = app.get(QueueService) as Mocked<QueueService>;
userWebhookService = app.get(UserWebhookService) as jest.Mocked<UserWebhookService>; userWebhookService = app.get(UserWebhookService) as Mocked<UserWebhookService>;
systemWebhookService = app.get(SystemWebhookService) as jest.Mocked<SystemWebhookService>; systemWebhookService = app.get(SystemWebhookService) as Mocked<SystemWebhookService>;
app.enableShutdownHooks(); app.enableShutdownHooks();
}); });

View File

@@ -9,8 +9,8 @@ import * as assert from 'assert';
import * as fs from 'node:fs'; import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path'; import { dirname } from 'node:path';
import { describe, beforeAll, beforeEach, test, vi } from 'vitest';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { jest } from '@jest/globals';
import { MockResolver } from '../misc/mock-resolver.js'; import { MockResolver } from '../misc/mock-resolver.js';
import type { IActor, IApDocument, ICollection, IObject, IPost } from '@/core/activitypub/type.js'; import type { IActor, IApDocument, ICollection, IObject, IPost } from '@/core/activitypub/type.js';
@@ -155,7 +155,7 @@ describe('ActivityPub', () => {
// Prevent ApPersonService from fetching instance, as it causes Jest import-after-test error // Prevent ApPersonService from fetching instance, as it causes Jest import-after-test error
const federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService); const federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService);
jest.spyOn(federatedInstanceService, 'fetch').mockImplementation(() => new Promise(() => { })); vi.spyOn(federatedInstanceService, 'fetch').mockImplementation(() => new Promise(() => { }));
}); });
beforeEach(() => { beforeEach(() => {

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { describe, test } from 'vitest';
import * as assert from 'assert'; import * as assert from 'assert';
import httpSignature from '@peertube/http-signature'; import httpSignature from '@peertube/http-signature';
@@ -78,7 +79,7 @@ describe('ap-request', () => {
'https://alice.example.com/abc', 'https://alice.example.com/abc',
FetchAllowSoftFailMask.Any, FetchAllowSoftFailMask.Any,
), 'validation should fail no matter what if the response URL is inconsistent with the object ID'); ), 'validation should fail no matter what if the response URL is inconsistent with the object ID');
assert.doesNotThrow(() => assertActivityMatchesUrl( assert.doesNotThrow(() => assertActivityMatchesUrl(
'https://alice.example.com/abc#test', 'https://alice.example.com/abc#test',
{ id: 'https://alice.example.com/abc' } as IObject, { id: 'https://alice.example.com/abc' } as IObject,

View File

@@ -6,7 +6,8 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { jest } from '@jest/globals'; import { describe, beforeEach, afterEach, afterAll, test } from 'vitest';
import type { Mocked } from 'vitest';
import * as lolex from '@sinonjs/fake-timers'; import * as lolex from '@sinonjs/fake-timers';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import * as Redis from 'ioredis'; import * as Redis from 'ioredis';
@@ -28,7 +29,7 @@ describe('Chart', () => {
let redisClient = { let redisClient = {
set: () => Promise.resolve('OK'), set: () => Promise.resolve('OK'),
get: () => Promise.resolve(null), get: () => Promise.resolve(null),
} as unknown as jest.Mocked<Redis.Redis>; } as unknown as Mocked<Redis.Redis>;
let testChart: TestChart; let testChart: TestChart;
let testGroupedChart: TestGroupedChart; let testGroupedChart: TestGroupedChart;

View File

@@ -5,7 +5,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import { afterAll, beforeAll, beforeEach, describe, expect, jest, test } from '@jest/globals'; import { afterAll, beforeAll, beforeEach, describe, expect, vi, test } from 'vitest';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import type { TestingModule } from '@nestjs/testing'; import type { TestingModule } from '@nestjs/testing';
import type { DriveFilesRepository, DriveFoldersRepository, UsersRepository } from '@/models/_.js'; import type { DriveFilesRepository, DriveFoldersRepository, UsersRepository } from '@/models/_.js';
@@ -30,13 +30,13 @@ describe('DriveFileEntityService', () => {
let idCounter = 0; let idCounter = 0;
const userEntityServiceMock = { const userEntityServiceMock = {
packMany: jest.fn(async (users: Array<string | { id: string }>) => { packMany: vi.fn(async (users: Array<string | { id: string }>) => {
return users.map(u => ({ return users.map(u => ({
id: typeof u === 'string' ? u : u.id, id: typeof u === 'string' ? u : u.id,
username: 'user', username: 'user',
})); }));
}), }),
pack: jest.fn(async (user: string | { id: string }) => { pack: vi.fn(async (user: string | { id: string }) => {
return { return {
id: typeof user === 'string' ? user : user.id, id: typeof user === 'string' ? user : user.id,
username: 'user', username: 'user',
@@ -195,7 +195,7 @@ describe('DriveFileEntityService', () => {
test('detail: true uses DriveFolderEntityService pack', async () => { test('detail: true uses DriveFolderEntityService pack', async () => {
const folder = await createFolder('packmany-folder', null); const folder = await createFolder('packmany-folder', null);
const file = await createFile(folder.id, null); const file = await createFile(folder.id, null);
const packSpy = jest.spyOn(driveFolderEntityService, 'pack'); const packSpy = vi.spyOn(driveFolderEntityService, 'pack');
await service.packMany([file], { detail: true, self: true }); await service.packMany([file], { detail: true, self: true });
expect(packSpy).toHaveBeenCalled(); expect(packSpy).toHaveBeenCalled();

View File

@@ -5,7 +5,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import { afterAll, beforeAll, describe, expect, test } from '@jest/globals'; import { afterAll, beforeAll, describe, expect, test } from 'vitest';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import type { TestingModule } from '@nestjs/testing'; import type { TestingModule } from '@nestjs/testing';
import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/_.js'; import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/_.js';

View File

@@ -4,6 +4,7 @@
*/ */
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { describe, expect, beforeAll, afterAll, test } from 'vitest';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { GlobalModule } from '@/GlobalModule.js'; import { GlobalModule } from '@/GlobalModule.js';

View File

@@ -4,6 +4,7 @@
*/ */
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, test } from 'vitest';
import { parse } from 'mfm-js'; import { parse } from 'mfm-js';
import { extractMentions } from '@/misc/extract-mentions.js'; import { extractMentions } from '@/misc/extract-mentions.js';

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { describe, expect, it } from 'vitest';
import { checkWordMute } from '@/misc/check-word-mute.js'; import { checkWordMute } from '@/misc/check-word-mute.js';
describe(checkWordMute, () => { describe(checkWordMute, () => {

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { describe, expect, it, test } from 'vitest';
import { correctFilename } from '@/misc/correct-filename.js'; import { correctFilename } from '@/misc/correct-filename.js';
describe(correctFilename, () => { describe(correctFilename, () => {

View File

@@ -4,7 +4,7 @@
*/ */
import { ulid } from 'ulid'; import { ulid } from 'ulid';
import { describe, expect, test } from '@jest/globals'; import { describe, expect, test } from 'vitest';
import { aidRegExp, genAid, parseAid } from '@/misc/id/aid.js'; import { aidRegExp, genAid, parseAid } from '@/misc/id/aid.js';
import { aidxRegExp, genAidx, parseAidx } from '@/misc/id/aidx.js'; import { aidxRegExp, genAidx, parseAidx } from '@/misc/id/aidx.js';
import { genMeid, meidRegExp, parseMeid } from '@/misc/id/meid.js'; import { genMeid, meidRegExp, parseMeid } from '@/misc/id/meid.js';

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { describe, expect, test } from 'vitest';
import { isQuote, isRenote } from '@/misc/is-renote.js'; import { isQuote, isRenote } from '@/misc/is-renote.js';
import { MiNote } from '@/models/Note.js'; import { MiNote } from '@/models/Note.js';

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { expect, describe, it } from 'vitest';
import { DebounceLoader } from '@/misc/loader.js'; import { DebounceLoader } from '@/misc/loader.js';
class Mock { class Mock {

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { describe, expect, test } from '@jest/globals'; import { describe, expect, test } from 'vitest';
import { contentDisposition } from '@/misc/content-disposition.js'; import { contentDisposition } from '@/misc/content-disposition.js';
describe('misc:content-disposition', () => { describe('misc:content-disposition', () => {

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { describe, expect, test, beforeEach, afterEach } from '@jest/globals'; import { describe, expect, test, beforeEach, afterEach } from 'vitest';
import * as lolex from '@sinonjs/fake-timers'; import * as lolex from '@sinonjs/fake-timers';
import { shouldHideNoteByTime } from '@/misc/should-hide-note-by-time.js'; import { shouldHideNoteByTime } from '@/misc/should-hide-note-by-time.js';

Some files were not shown because too many files have changed in this diff Show More