fix(deps): update [backend] update dependencies (major) (#16099)

* fix(deps): update [backend] update dependencies

* update approve builds

* update minimum node version for testing

* remove types/bcryptjs

* fix(backend): remove removed type previously exported from file-type

* migrate webauthnservice

* Update Changelog

* update deps (MisskeyIO#889)

- メンテナンスされないredis-lockを自前実装に変更
- 既にロックされている場合のリトライ間隔を調整

* use main redis for lock

* spdx

* tweak max retries

* [ci skip] dedupe

* attempt to fix test

* attempt to fix test

* Revert "attempt to fix test"

This reverts commit c508318627.

* temporarily roll back simonjs/fake-timers to v11.3.1

* Revert "temporarily roll back simonjs/fake-timers to v11.3.1"

This reverts commit 54f1fc3d79.

* migrate sinonjs/fake-timers

* update deps / migrate jest 30

* fix test

* fix: update node.js min version to 20.18.1

* fix: rollback nsfwjs to 4.2.0

* fix

* attempt to fix test

* attempt to fix test

* attempt to fix test

* attempt to fix test

* revert jest 30 related changes

* update deps

* fix test

* fix: rollback nsfwjs to 4.2.0

* fix: rollback sharp to 0.33

* update deps

* fix: rollback sharp-read-bmp to 1.2.0

* fix: rollback nsfwjs to 4.2.0

* recreate lockfile

* update deps

* fix: rollback sharp-read-bmp to 1.2.0

* fix: rollback jsdom, parse5

* fix: rollback jsdom types

* fix [ci skip]

* run pnpm dedupe

* update deps

* run pnpm dedupe [ci skip]

* Update Changelog [ci skip]

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
Co-authored-by: あわわわとーにゅ <17376330+u1-liquid@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
renovate[bot] 2025-11-30 09:58:06 +09:00 committed by GitHub
parent 1ae8e7900d
commit 0455187a68
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 879 additions and 740 deletions

View File

@ -9,6 +9,7 @@
### Server
- Enhance: メモリ使用量を削減しました
- Enhance: ActivityPubアクティビティを送信する際のパフォーマンス向上
- Enhance: 依存関係の更新
## 2025.11.1

View File

@ -40,17 +40,17 @@
},
"optionalDependencies": {
"@swc/core-android-arm64": "1.3.11",
"@swc/core-darwin-arm64": "1.15.2",
"@swc/core-darwin-x64": "1.15.2",
"@swc/core-darwin-arm64": "1.15.3",
"@swc/core-darwin-x64": "1.15.3",
"@swc/core-freebsd-x64": "1.3.11",
"@swc/core-linux-arm-gnueabihf": "1.15.2",
"@swc/core-linux-arm64-gnu": "1.15.2",
"@swc/core-linux-arm64-musl": "1.15.2",
"@swc/core-linux-x64-gnu": "1.15.2",
"@swc/core-linux-x64-musl": "1.15.2",
"@swc/core-win32-arm64-msvc": "1.15.2",
"@swc/core-win32-ia32-msvc": "1.15.2",
"@swc/core-win32-x64-msvc": "1.15.2",
"@swc/core-linux-arm-gnueabihf": "1.15.3",
"@swc/core-linux-arm64-gnu": "1.15.3",
"@swc/core-linux-arm64-musl": "1.15.3",
"@swc/core-linux-x64-gnu": "1.15.3",
"@swc/core-linux-x64-musl": "1.15.3",
"@swc/core-win32-arm64-msvc": "1.15.3",
"@swc/core-win32-ia32-msvc": "1.15.3",
"@swc/core-win32-x64-msvc": "1.15.3",
"@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0",
"bufferutil": "4.0.9",
@ -70,17 +70,17 @@
"utf-8-validate": "6.0.5"
},
"dependencies": {
"@aws-sdk/client-s3": "3.936.0",
"@aws-sdk/lib-storage": "3.936.0",
"@aws-sdk/client-s3": "3.937.0",
"@aws-sdk/lib-storage": "3.937.0",
"@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.3",
"@fastify/cookie": "11.0.2",
"@fastify/cors": "10.1.0",
"@fastify/cors": "11.1.0",
"@fastify/express": "4.0.2",
"@fastify/http-proxy": "10.0.2",
"@fastify/http-proxy": "11.3.0",
"@fastify/multipart": "9.3.0",
"@fastify/static": "8.3.0",
"@fastify/view": "10.0.2",
"@fastify/view": "11.1.1",
"@misskey-dev/sharp-read-bmp": "1.2.0",
"@misskey-dev/summaly": "5.2.5",
"@napi-rs/canvas": "0.1.82",
@ -90,33 +90,33 @@
"@peertube/http-signature": "1.7.0",
"@sentry/node": "10.26.0",
"@sentry/profiling-node": "10.26.0",
"@simplewebauthn/server": "12.0.0",
"@sinonjs/fake-timers": "11.3.1",
"@smithy/node-http-handler": "2.5.0",
"@simplewebauthn/server": "13.2.2",
"@sinonjs/fake-timers": "15.0.0",
"@smithy/node-http-handler": "4.4.5",
"@swc/cli": "0.7.9",
"@swc/core": "1.15.2",
"@swc/core": "1.15.3",
"@twemoji/parser": "16.0.0",
"@types/redis-info": "3.0.3",
"accepts": "1.3.8",
"ajv": "8.17.1",
"archiver": "7.0.1",
"async-mutex": "0.5.0",
"bcryptjs": "2.4.3",
"bcryptjs": "3.0.3",
"blurhash": "2.0.5",
"body-parser": "1.20.3",
"bullmq": "5.63.2",
"body-parser": "2.2.0",
"bullmq": "5.64.1",
"cacheable-lookup": "7.0.0",
"cbor": "9.0.2",
"cbor": "10.0.11",
"chalk": "5.6.2",
"chalk-template": "1.1.2",
"chokidar": "4.0.3",
"color-convert": "2.0.1",
"content-disposition": "0.5.4",
"date-fns": "2.30.0",
"color-convert": "3.1.3",
"content-disposition": "1.0.1",
"date-fns": "4.1.0",
"deep-email-validator": "0.1.21",
"fastify": "5.6.2",
"fastify-raw-body": "5.0.0",
"feed": "4.2.2",
"feed": "5.1.0",
"file-type": "21.1.1",
"fluent-ffmpeg": "2.1.3",
"form-data": "4.0.5",
@ -126,16 +126,16 @@
"ioredis": "5.8.2",
"ip-cidr": "4.0.2",
"ipaddr.js": "2.2.0",
"is-svg": "5.1.0",
"is-svg": "6.1.0",
"js-yaml": "4.1.1",
"json5": "2.2.3",
"jsonld": "8.3.3",
"jsonld": "9.0.0",
"jsrsasign": "11.1.0",
"juice": "11.0.3",
"meilisearch": "0.54.0",
"mfm-js": "0.25.0",
"microformats-parser": "2.0.4",
"mime-types": "2.1.35",
"mime-types": "3.0.2",
"misskey-js": "workspace:*",
"misskey-reversi": "workspace:*",
"ms": "3.0.0-canary.202508261828",
@ -151,7 +151,7 @@
"os-utils": "0.0.14",
"otpauth": "9.4.1",
"pg": "8.16.3",
"pkce-challenge": "4.1.0",
"pkce-challenge": "5.0.0",
"probe-image-size": "7.2.3",
"promise-limit": "2.7.0",
"pug": "3.0.3",
@ -160,13 +160,12 @@
"ratelimiter": "3.4.1",
"re2": "1.22.3",
"redis-info": "3.1.0",
"redis-lock": "0.1.4",
"reflect-metadata": "0.2.2",
"rename": "1.0.4",
"rss-parser": "3.13.0",
"rxjs": "7.8.2",
"sanitize-html": "2.17.0",
"secure-json-parse": "3.0.2",
"secure-json-parse": "4.1.0",
"semver": "7.7.3",
"sharp": "0.33.5",
"slacc": "0.0.10",
@ -179,7 +178,7 @@
"tsconfig-paths": "4.2.0",
"typeorm": "0.3.27",
"typescript": "5.9.3",
"ulid": "2.4.0",
"ulid": "3.0.1",
"vary": "1.1.2",
"web-push": "3.6.7",
"ws": "8.18.3",
@ -187,13 +186,12 @@
},
"devDependencies": {
"@jest/globals": "29.7.0",
"@nestjs/platform-express": "10.4.20",
"@nestjs/platform-express": "11.1.9",
"@sentry/vue": "10.26.0",
"@simplewebauthn/types": "12.0.0",
"@swc/jest": "0.2.39",
"@types/accepts": "1.3.7",
"@types/archiver": "6.0.4",
"@types/bcryptjs": "2.4.6",
"@types/archiver": "7.0.0",
"@types/body-parser": "1.19.6",
"@types/color-convert": "2.0.4",
"@types/content-disposition": "0.5.9",
@ -203,10 +201,10 @@
"@types/js-yaml": "4.0.9",
"@types/jsonld": "1.5.15",
"@types/jsrsasign": "10.5.15",
"@types/mime-types": "2.1.4",
"@types/ms": "0.7.34",
"@types/mime-types": "3.0.1",
"@types/ms": "2.1.0",
"@types/node": "24.10.1",
"@types/nodemailer": "6.4.21",
"@types/nodemailer": "7.0.4",
"@types/oauth": "0.9.6",
"@types/oauth2orize": "1.11.5",
"@types/oauth2orize-pkce": "0.1.2",
@ -219,7 +217,7 @@
"@types/sanitize-html": "2.16.0",
"@types/semver": "7.7.1",
"@types/simple-oauth2": "5.0.7",
"@types/sinonjs__fake-timers": "8.1.5",
"@types/sinonjs__fake-timers": "15.0.1",
"@types/supertest": "6.0.3",
"@types/tinycolor2": "1.4.6",
"@types/tmp": "0.2.6",
@ -229,14 +227,15 @@
"@typescript-eslint/eslint-plugin": "8.47.0",
"@typescript-eslint/parser": "8.47.0",
"aws-sdk-client-mock": "4.1.0",
"cross-env": "7.0.3",
"cross-env": "10.1.0",
"eslint-plugin-import": "2.32.0",
"execa": "8.0.1",
"fkill": "9.0.0",
"execa": "9.6.0",
"fkill": "10.0.1",
"jest": "29.7.0",
"jest-mock": "29.7.0",
"jest-util": "29.7.0",
"nodemon": "3.1.11",
"pid-port": "1.0.2",
"pid-port": "2.0.0",
"simple-oauth2": "5.1.0",
"supertest": "7.1.4"
}

View File

@ -1,13 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
declare module 'redis-lock' {
import type Redis from 'ioredis';
type Lock = (lockName: string, timeout?: number, taskToPerform?: () => Promise<void>) => void;
function redisLock(client: Redis.Redis, retryDelay: number): Lock;
export = redisLock;
}

View File

@ -1,44 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { promisify } from 'node:util';
import { Inject, Injectable } from '@nestjs/common';
import redisLock from 'redis-lock';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
/**
* Retry delay (ms) for lock acquisition
*/
const retryDelay = 100;
@Injectable()
export class AppLockService {
private lock: (key: string, timeout?: number, _?: (() => Promise<void>) | undefined) => Promise<() => void>;
constructor(
@Inject(DI.redis)
private redisClient: Redis.Redis,
) {
this.lock = promisify(redisLock(this.redisClient, retryDelay));
}
/**
* Get AP Object lock
* @param uri AP object ID
* @param timeout Lock timeout (ms), The timeout releases previous lock.
* @returns Unlock function
*/
@bindThis
public getApLock(uri: string, timeout = 30 * 1000): Promise<() => void> {
return this.lock(`ap-object:${uri}`, timeout);
}
@bindThis
public getChartInsertLock(lockKey: string, timeout = 30 * 1000): Promise<() => void> {
return this.lock(`chart-insert:${lockKey}`, timeout);
}
}

View File

@ -21,7 +21,6 @@ import { AccountUpdateService } from './AccountUpdateService.js';
import { AiService } from './AiService.js';
import { AnnouncementService } from './AnnouncementService.js';
import { AntennaService } from './AntennaService.js';
import { AppLockService } from './AppLockService.js';
import { AchievementService } from './AchievementService.js';
import { AvatarDecorationService } from './AvatarDecorationService.js';
import { CaptchaService } from './CaptchaService.js';
@ -166,7 +165,6 @@ const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useEx
const $AiService: Provider = { provide: 'AiService', useExisting: AiService };
const $AnnouncementService: Provider = { provide: 'AnnouncementService', useExisting: AnnouncementService };
const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService };
const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppLockService };
const $AchievementService: Provider = { provide: 'AchievementService', useExisting: AchievementService };
const $AvatarDecorationService: Provider = { provide: 'AvatarDecorationService', useExisting: AvatarDecorationService };
const $CaptchaService: Provider = { provide: 'CaptchaService', useExisting: CaptchaService };
@ -320,7 +318,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
AiService,
AnnouncementService,
AntennaService,
AppLockService,
AchievementService,
AvatarDecorationService,
CaptchaService,
@ -470,7 +467,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$AiService,
$AnnouncementService,
$AntennaService,
$AppLockService,
$AchievementService,
$AvatarDecorationService,
$CaptchaService,
@ -621,7 +617,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
AiService,
AnnouncementService,
AntennaService,
AppLockService,
AchievementService,
AvatarDecorationService,
CaptchaService,
@ -770,7 +765,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$AiService,
$AnnouncementService,
$AntennaService,
$AppLockService,
$AchievementService,
$AvatarDecorationService,
$CaptchaService,

View File

@ -66,7 +66,6 @@ export class WebAuthnService {
userID: isoUint8Array.fromUTF8String(userId),
userName: userName,
userDisplayName: userDisplayName,
attestationType: 'indirect',
excludeCredentials: keys.map(key => (<{ id: string; transports?: AuthenticatorTransportFuture[]; }>{
id: key.id,
transports: key.transports ?? undefined,

View File

@ -5,6 +5,7 @@
import { Inject, Injectable } from '@nestjs/common';
import { In } from 'typeorm';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import { UserFollowingService } from '@/core/UserFollowingService.js';
@ -14,8 +15,8 @@ import { NotePiningService } from '@/core/NotePiningService.js';
import { UserBlockingService } from '@/core/UserBlockingService.js';
import { NoteDeleteService } from '@/core/NoteDeleteService.js';
import { NoteCreateService } from '@/core/NoteCreateService.js';
import { acquireApObjectLock } from '@/misc/distributed-lock.js';
import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.js';
import { AppLockService } from '@/core/AppLockService.js';
import type Logger from '@/logger.js';
import { IdService } from '@/core/IdService.js';
import { StatusError } from '@/misc/status-error.js';
@ -48,8 +49,8 @@ export class ApInboxService {
@Inject(DI.config)
private config: Config,
@Inject(DI.meta)
private meta: MiMeta,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@ -76,7 +77,6 @@ export class ApInboxService {
private userBlockingService: UserBlockingService,
private noteCreateService: NoteCreateService,
private noteDeleteService: NoteDeleteService,
private appLockService: AppLockService,
private apResolverService: ApResolverService,
private apDbResolverService: ApDbResolverService,
private apLoggerService: ApLoggerService,
@ -311,7 +311,7 @@ export class ApInboxService {
// アナウンス先が許可されているかチェック
if (!this.utilityService.isFederationAllowedUri(uri)) return;
const unlock = await this.appLockService.getApLock(uri);
const unlock = await acquireApObjectLock(this.redisClient, uri);
try {
// 既に同じURIを持つものが登録されていないかチェック
@ -438,7 +438,7 @@ export class ApInboxService {
}
}
const unlock = await this.appLockService.getApLock(uri);
const unlock = await acquireApObjectLock(this.redisClient, uri);
try {
const exist = await this.apNoteService.fetchNote(note);
@ -522,7 +522,7 @@ export class ApInboxService {
private async deleteNote(actor: MiRemoteUser, uri: string): Promise<string> {
this.logger.info(`Deleting the Note: ${uri}`);
const unlock = await this.appLockService.getApLock(uri);
const unlock = await acquireApObjectLock(this.redisClient, uri);
try {
const note = await this.apDbResolverService.getNoteFromApId(uri);

View File

@ -5,14 +5,15 @@
import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { In } from 'typeorm';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import type { PollsRepository, EmojisRepository, MiMeta } from '@/models/_.js';
import type { Config } from '@/config.js';
import type { MiRemoteUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js';
import { acquireApObjectLock } from '@/misc/distributed-lock.js';
import { toArray, toSingle, unique } from '@/misc/prelude/array.js';
import type { MiEmoji } from '@/models/Emoji.js';
import { AppLockService } from '@/core/AppLockService.js';
import type { MiDriveFile } from '@/models/DriveFile.js';
import { NoteCreateService } from '@/core/NoteCreateService.js';
import type Logger from '@/logger.js';
@ -48,6 +49,9 @@ export class ApNoteService {
@Inject(DI.meta)
private meta: MiMeta,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.pollsRepository)
private pollsRepository: PollsRepository,
@ -67,7 +71,6 @@ export class ApNoteService {
private apMentionService: ApMentionService,
private apImageService: ApImageService,
private apQuestionService: ApQuestionService,
private appLockService: AppLockService,
private pollService: PollService,
private noteCreateService: NoteCreateService,
private apDbResolverService: ApDbResolverService,
@ -354,7 +357,7 @@ export class ApNoteService {
throw new StatusError('blocked host', 451);
}
const unlock = await this.appLockService.getApLock(uri);
const unlock = await acquireApObjectLock(this.redisClient, uri);
try {
//#region このサーバーに既に登録されていたらそれを返す

View File

@ -5,11 +5,12 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js';
import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/active-users.js';
@ -28,11 +29,13 @@ export default class ActiveUsersChart extends Chart<typeof schema> { // eslint-d
@Inject(DI.db)
private db: DataSource,
private appLockService: AppLockService,
@Inject(DI.redis)
private redisClient: Redis.Redis,
private chartLoggerService: ChartLoggerService,
private idService: IdService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,9 +5,10 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/ap-request.js';
@ -22,10 +23,12 @@ export default class ApRequestChart extends Chart<typeof schema> { // eslint-dis
@Inject(DI.db)
private db: DataSource,
private appLockService: AppLockService,
@Inject(DI.redis)
private redisClient: Redis.Redis,
private chartLoggerService: ChartLoggerService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { MiDriveFile } from '@/models/DriveFile.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/drive.js';
@ -23,10 +24,12 @@ export default class DriveChart extends Chart<typeof schema> { // eslint-disable
@Inject(DI.db)
private db: DataSource,
private appLockService: AppLockService,
@Inject(DI.redis)
private redisClient: Redis.Redis,
private chartLoggerService: ChartLoggerService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { FollowingsRepository, InstancesRepository, MiMeta } from '@/models/_.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/federation.js';
@ -26,16 +27,18 @@ export default class FederationChart extends Chart<typeof schema> { // eslint-di
@Inject(DI.meta)
private meta: MiMeta,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository,
@Inject(DI.instancesRepository)
private instancesRepository: InstancesRepository,
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,13 +5,14 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { DriveFilesRepository, FollowingsRepository, UsersRepository, NotesRepository } from '@/models/_.js';
import type { MiDriveFile } from '@/models/DriveFile.js';
import type { MiNote } from '@/models/Note.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { UtilityService } from '@/core/UtilityService.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/instance.js';
@ -26,6 +27,9 @@ export default class InstanceChart extends Chart<typeof schema> { // eslint-disa
@Inject(DI.db)
private db: DataSource,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@ -39,10 +43,9 @@ export default class InstanceChart extends Chart<typeof schema> { // eslint-disa
private followingsRepository: FollowingsRepository,
private utilityService: UtilityService,
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,11 +5,12 @@
import { Injectable, Inject } from '@nestjs/common';
import { Not, IsNull, DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { NotesRepository } from '@/models/_.js';
import type { MiNote } from '@/models/Note.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/notes.js';
@ -24,13 +25,15 @@ export default class NotesChart extends Chart<typeof schema> { // eslint-disable
@Inject(DI.db)
private db: DataSource,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { DriveFilesRepository } from '@/models/_.js';
import type { MiDriveFile } from '@/models/DriveFile.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-drive.js';
@ -25,14 +26,16 @@ export default class PerUserDriveChart extends Chart<typeof schema> { // eslint-
@Inject(DI.db)
private db: DataSource,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.driveFilesRepository)
private driveFilesRepository: DriveFilesRepository,
private appLockService: AppLockService,
private driveFileEntityService: DriveFileEntityService,
private chartLoggerService: ChartLoggerService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common';
import { Not, IsNull, DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import type { FollowingsRepository } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-following.js';
@ -25,14 +26,16 @@ export default class PerUserFollowingChart extends Chart<typeof schema> { // esl
@Inject(DI.db)
private db: DataSource,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository,
private appLockService: AppLockService,
private userEntityService: UserEntityService,
private chartLoggerService: ChartLoggerService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import type { NotesRepository } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-notes.js';
@ -25,13 +26,15 @@ export default class PerUserNotesChart extends Chart<typeof schema> { // eslint-
@Inject(DI.db)
private db: DataSource,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-pv.js';
@ -23,10 +24,12 @@ export default class PerUserPvChart extends Chart<typeof schema> { // eslint-dis
@Inject(DI.db)
private db: DataSource,
private appLockService: AppLockService,
@Inject(DI.redis)
private redisClient: Redis.Redis,
private chartLoggerService: ChartLoggerService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-reactions.js';
@ -25,11 +26,13 @@ export default class PerUserReactionsChart extends Chart<typeof schema> { // esl
@Inject(DI.db)
private db: DataSource,
private appLockService: AppLockService,
@Inject(DI.redis)
private redisClient: Redis.Redis,
private userEntityService: UserEntityService,
private chartLoggerService: ChartLoggerService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { name, schema } from './entities/test-grouped.js';
import type { KVs } from '../core.js';
@ -24,10 +25,12 @@ export default class TestGroupedChart extends Chart<typeof schema> { // eslint-d
@Inject(DI.db)
private db: DataSource,
private appLockService: AppLockService,
@Inject(DI.redis)
private redisClient: Redis.Redis,
logger: Logger,
) {
super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema, true);
super(db, (k) => acquireChartInsertLock(redisClient, k), logger, name, schema, true);
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { name, schema } from './entities/test-intersection.js';
import type { KVs } from '../core.js';
@ -22,10 +23,12 @@ export default class TestIntersectionChart extends Chart<typeof schema> { // esl
@Inject(DI.db)
private db: DataSource,
private appLockService: AppLockService,
@Inject(DI.redis)
private redisClient: Redis.Redis,
logger: Logger,
) {
super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema);
super(db, (k) => acquireChartInsertLock(redisClient, k), logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { name, schema } from './entities/test-unique.js';
import type { KVs } from '../core.js';
@ -22,10 +23,12 @@ export default class TestUniqueChart extends Chart<typeof schema> { // eslint-di
@Inject(DI.db)
private db: DataSource,
private appLockService: AppLockService,
@Inject(DI.redis)
private redisClient: Redis.Redis,
logger: Logger,
) {
super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema);
super(db, (k) => acquireChartInsertLock(redisClient, k), logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { name, schema } from './entities/test.js';
import type { KVs } from '../core.js';
@ -24,10 +25,12 @@ export default class TestChart extends Chart<typeof schema> { // eslint-disable-
@Inject(DI.db)
private db: DataSource,
private appLockService: AppLockService,
@Inject(DI.redis)
private redisClient: Redis.Redis,
logger: Logger,
) {
super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema);
super(db, (k) => acquireChartInsertLock(redisClient, k), logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common';
import { Not, IsNull, DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import type { UsersRepository } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/users.js';
@ -25,14 +26,16 @@ export default class UsersChart extends Chart<typeof schema> { // eslint-disable
@Inject(DI.db)
private db: DataSource,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
private appLockService: AppLockService,
private userEntityService: UserEntityService,
private chartLoggerService: ChartLoggerService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as Redis from 'ioredis';
export async function acquireDistributedLock(
redis: Redis.Redis,
name: string,
timeout: number,
maxRetries: number,
retryInterval: number,
): Promise<() => Promise<void>> {
const lockKey = `lock:${name}`;
const identifier = Math.random().toString(36).slice(2);
let retries = 0;
while (retries < maxRetries) {
const result = await redis.set(lockKey, identifier, 'PX', timeout, 'NX');
if (result === 'OK') {
return async () => {
const currentIdentifier = await redis.get(lockKey);
if (currentIdentifier === identifier) {
await redis.del(lockKey);
}
};
}
await new Promise(resolve => setTimeout(resolve, retryInterval));
retries++;
}
throw new Error(`Failed to acquire lock ${name}`);
}
export function acquireApObjectLock(
redis: Redis.Redis,
uri: string,
): Promise<() => Promise<void>> {
return acquireDistributedLock(redis, `ap-object:${uri}`, 30 * 1000, 50, 100);
}
export function acquireChartInsertLock(
redis: Redis.Redis,
name: string,
): Promise<() => Promise<void>> {
return acquireDistributedLock(redis, `chart-insert:${name}`, 30 * 1000, 50, 500);
}

View File

@ -9,3 +9,4 @@ beforeAll(async () => {
await initTestDb(false);
await sendEnvResetRequest();
});

View File

@ -26,7 +26,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { secureRndstr } from '@/misc/secure-rndstr.js';
import type { TestingModule } from '@nestjs/testing';
import type { MockFunctionMetadata } from 'jest-mock';
import type { MockMetadata } from 'jest-mock';
const moduleMocker = new ModuleMocker(global);
@ -84,7 +84,7 @@ describe('AnnouncementService', () => {
log: jest.fn(),
};
} else if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
}

View File

@ -446,7 +446,7 @@ describe('CaptchaService', () => {
if (!res.success) {
expect(res.error.code).toBe(code);
}
expect(metaService.update).not.toBeCalled();
expect(metaService.update).not.toHaveBeenCalled();
}
describe('invalidParameters', () => {

View File

@ -53,7 +53,7 @@ describe('DriveService', () => {
s3Mock.on(DeleteObjectCommand)
.rejects(new InvalidObjectState({ $metadata: {}, message: '' }));
await expect(driveService.deleteObjectStorageFile('unexpected')).rejects.toThrowError(Error);
await expect(driveService.deleteObjectStorageFile('unexpected')).rejects.toThrow(Error);
});
test('delete a file with no valid key', async () => {

View File

@ -17,7 +17,7 @@ import { FileInfo, FileInfoService } from '@/core/FileInfoService.js';
import { AiService } from '@/core/AiService.js';
import { LoggerService } from '@/core/LoggerService.js';
import type { TestingModule } from '@nestjs/testing';
import type { MockFunctionMetadata } from 'jest-mock';
import type { MockMetadata } from 'jest-mock';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
@ -34,7 +34,7 @@ describe('FileInfoService', () => {
delete fi.sensitive;
delete fi.blurhash;
delete fi.porn;
return fi;
}
@ -54,7 +54,7 @@ describe('FileInfoService', () => {
// return { };
//}
if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
}

View File

@ -9,7 +9,7 @@ import { jest } from '@jest/globals';
import { Test } from '@nestjs/testing';
import { ModuleMocker } from 'jest-mock';
import type { TestingModule } from '@nestjs/testing';
import type { MockFunctionMetadata } from 'jest-mock';
import type { MockMetadata } from 'jest-mock';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { IdService } from '@/core/IdService.js';
@ -45,7 +45,7 @@ describe('RelayService', () => {
return { deliver: jest.fn() };
}
if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
}

View File

@ -11,7 +11,7 @@ import { ModuleMocker } from 'jest-mock';
import { Test } from '@nestjs/testing';
import * as lolex from '@sinonjs/fake-timers';
import type { TestingModule } from '@nestjs/testing';
import type { MockFunctionMetadata } from 'jest-mock';
import type { MockMetadata } from 'jest-mock';
import { GlobalModule } from '@/GlobalModule.js';
import { RoleService } from '@/core/RoleService.js';
import {
@ -104,6 +104,8 @@ describe('RoleService', () => {
beforeEach(async () => {
clock = lolex.install({
// https://github.com/sinonjs/sinon/issues/2620
toFake: Object.keys(lolex.timers).filter((key) => !['nextTick', 'queueMicrotask'].includes(key)) as lolex.FakeMethod[],
now: new Date(),
shouldClearNativeTimers: true,
});
@ -135,7 +137,7 @@ describe('RoleService', () => {
return { fetch: jest.fn() };
}
if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
}

View File

@ -72,7 +72,7 @@ describe('S3Service', () => {
Bucket: 'fake',
Key: 'fake',
Body: 'x',
})).rejects.toThrowError(Error);
})).rejects.toThrow(Error);
});
test('upload a large file error', async () => {
@ -82,7 +82,7 @@ describe('S3Service', () => {
Bucket: 'fake',
Key: 'fake',
Body: 'x'.repeat(8 * 1024 * 1024 + 1), // デフォルトpartSizeにしている 8 * 1024 * 1024 を越えるサイズ
})).rejects.toThrowError(Error);
})).rejects.toThrow(Error);
});
});
});

View File

@ -9,7 +9,7 @@ import { Test, TestingModule } from '@nestjs/testing';
import { FastifyReply, FastifyRequest } from 'fastify';
import { AuthenticationResponseJSON } from '@simplewebauthn/types';
import { HttpHeader } from 'fastify/types/utils.js';
import { MockFunctionMetadata, ModuleMocker } from 'jest-mock';
import { MockMetadata, ModuleMocker } from 'jest-mock';
import { MiUser } from '@/models/User.js';
import { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js';
import { IdService } from '@/core/IdService.js';
@ -95,7 +95,7 @@ describe('SigninWithPasskeyApiService', () => {
],
}).useMocker((token) => {
if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
}

View File

@ -9,6 +9,7 @@ import * as assert from 'assert';
import { jest } from '@jest/globals';
import * as lolex from '@sinonjs/fake-timers';
import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import TestChart from '@/core/chart/charts/test.js';
import TestGroupedChart from '@/core/chart/charts/test-grouped.js';
import TestUniqueChart from '@/core/chart/charts/test-unique.js';
@ -18,16 +19,16 @@ import { entity as TestGroupedChartEntity } from '@/core/chart/charts/entities/t
import { entity as TestUniqueChartEntity } from '@/core/chart/charts/entities/test-unique.js';
import { entity as TestIntersectionChartEntity } from '@/core/chart/charts/entities/test-intersection.js';
import { loadConfig } from '@/config.js';
import type { AppLockService } from '@/core/AppLockService.js';
import Logger from '@/logger.js';
describe('Chart', () => {
const config = loadConfig();
const appLockService = {
getChartInsertLock: () => () => Promise.resolve(() => {}),
} as unknown as jest.Mocked<AppLockService>;
let db: DataSource | undefined;
let redisClient = {
set: () => Promise.resolve('OK'),
get: () => Promise.resolve(null),
} as unknown as jest.Mocked<Redis.Redis>;
let testChart: TestChart;
let testGroupedChart: TestGroupedChart;
@ -64,12 +65,14 @@ describe('Chart', () => {
await db.initialize();
const logger = new Logger('chart'); // TODO: モックにする
testChart = new TestChart(db, appLockService, logger);
testGroupedChart = new TestGroupedChart(db, appLockService, logger);
testUniqueChart = new TestUniqueChart(db, appLockService, logger);
testIntersectionChart = new TestIntersectionChart(db, appLockService, logger);
testChart = new TestChart(db, redisClient, logger);
testGroupedChart = new TestGroupedChart(db, redisClient, logger);
testUniqueChart = new TestUniqueChart(db, redisClient, logger);
testIntersectionChart = new TestIntersectionChart(db, redisClient, logger);
clock = lolex.install({
// https://github.com/sinonjs/sinon/issues/2620
toFake: Object.keys(lolex.timers).filter((key) => !['nextTick', 'queueMicrotask'].includes(key)) as lolex.FakeMethod[],
now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0)),
shouldClearNativeTimers: true,
});

View File

@ -141,6 +141,8 @@ describe('CheckModeratorsActivityProcessorService', () => {
beforeEach(async () => {
clock = lolex.install({
// https://github.com/sinonjs/sinon/issues/2620
toFake: Object.keys(lolex.timers).filter((key) => !['nextTick', 'queueMicrotask'].includes(key)) as lolex.FakeMethod[],
now: new Date(baseDate),
shouldClearNativeTimers: true,
});

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@ onlyBuiltDependencies:
- '@nestjs/core'
- '@parcel/watcher'
- '@sentry/profiling-node'
- '@sentry-internal/node-cpu-profiler'
- '@swc/core'
- '@tensorflow/tfjs-node'
- bufferutil