Merge branch 'develop' into allow-suspended-access
This commit is contained in:
@@ -9,8 +9,16 @@ import * as assert from 'assert';
|
||||
import * as crypto from 'node:crypto';
|
||||
import cbor from 'cbor';
|
||||
import * as OTPAuth from 'otpauth';
|
||||
import { loadConfig } from '../../src/config.js';
|
||||
import { signup, api, post, react, startServer, waitFire } from '../utils.js';
|
||||
import { loadConfig } from '@/config.js';
|
||||
import { api, signup, startServer } from '../utils.js';
|
||||
import type {
|
||||
AuthenticationResponseJSON,
|
||||
AuthenticatorAssertionResponseJSON,
|
||||
AuthenticatorAttestationResponseJSON,
|
||||
PublicKeyCredentialCreationOptionsJSON,
|
||||
PublicKeyCredentialRequestOptionsJSON,
|
||||
RegistrationResponseJSON,
|
||||
} from '@simplewebauthn/typescript-types';
|
||||
import type { INestApplicationContext } from '@nestjs/common';
|
||||
import type * as misskey from 'misskey-js';
|
||||
|
||||
@@ -47,21 +55,20 @@ describe('2要素認証', () => {
|
||||
|
||||
const rpIdHash = (): Buffer => {
|
||||
return crypto.createHash('sha256')
|
||||
.update(Buffer.from(config.hostname, 'utf-8'))
|
||||
.update(Buffer.from(config.host, 'utf-8'))
|
||||
.digest();
|
||||
};
|
||||
|
||||
const keyDoneParam = (param: {
|
||||
token: string,
|
||||
keyName: string,
|
||||
challengeId: string,
|
||||
challenge: string,
|
||||
credentialId: Buffer,
|
||||
creationOptions: PublicKeyCredentialCreationOptionsJSON,
|
||||
}): {
|
||||
attestationObject: string,
|
||||
challengeId: string,
|
||||
clientDataJSON: string,
|
||||
token: string,
|
||||
password: string,
|
||||
name: string,
|
||||
credential: RegistrationResponseJSON,
|
||||
} => {
|
||||
// A COSE encoded public key
|
||||
const credentialPublicKey = cbor.encode(new Map<number, unknown>([
|
||||
@@ -76,7 +83,7 @@ describe('2要素認証', () => {
|
||||
// AuthenticatorAssertionResponse.authenticatorData
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/AuthenticatorAssertionResponse/authenticatorData
|
||||
const credentialIdLength = Buffer.allocUnsafe(2);
|
||||
credentialIdLength.writeUInt16BE(param.credentialId.length);
|
||||
credentialIdLength.writeUInt16BE(param.credentialId.length, 0);
|
||||
const authData = Buffer.concat([
|
||||
rpIdHash(), // rpIdHash(32)
|
||||
Buffer.from([0x45]), // flags(1)
|
||||
@@ -88,20 +95,28 @@ describe('2要素認証', () => {
|
||||
]);
|
||||
|
||||
return {
|
||||
attestationObject: cbor.encode({
|
||||
fmt: 'none',
|
||||
attStmt: {},
|
||||
authData,
|
||||
}).toString('hex'),
|
||||
challengeId: param.challengeId,
|
||||
clientDataJSON: JSON.stringify({
|
||||
type: 'webauthn.create',
|
||||
challenge: param.challenge,
|
||||
origin: config.scheme + '://' + config.host,
|
||||
androidPackageName: 'org.mozilla.firefox',
|
||||
}),
|
||||
password,
|
||||
token: param.token,
|
||||
name: param.keyName,
|
||||
credential: <RegistrationResponseJSON>{
|
||||
id: param.credentialId.toString('base64url'),
|
||||
rawId: param.credentialId.toString('base64url'),
|
||||
response: <AuthenticatorAttestationResponseJSON>{
|
||||
clientDataJSON: Buffer.from(JSON.stringify({
|
||||
type: 'webauthn.create',
|
||||
challenge: param.creationOptions.challenge,
|
||||
origin: config.scheme + '://' + config.host,
|
||||
androidPackageName: 'org.mozilla.firefox',
|
||||
}), 'utf-8').toString('base64url'),
|
||||
attestationObject: cbor.encode({
|
||||
fmt: 'none',
|
||||
attStmt: {},
|
||||
authData,
|
||||
}).toString('base64url'),
|
||||
},
|
||||
clientExtensionResults: {},
|
||||
type: 'public-key',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -121,17 +136,12 @@ describe('2要素認証', () => {
|
||||
|
||||
const signinWithSecurityKeyParam = (param: {
|
||||
keyName: string,
|
||||
challengeId: string,
|
||||
challenge: string,
|
||||
credentialId: Buffer,
|
||||
requestOptions: PublicKeyCredentialRequestOptionsJSON,
|
||||
}): {
|
||||
authenticatorData: string,
|
||||
credentialId: string,
|
||||
challengeId: string,
|
||||
clientDataJSON: string,
|
||||
username: string,
|
||||
password: string,
|
||||
signature: string,
|
||||
credential: AuthenticationResponseJSON,
|
||||
'g-recaptcha-response'?: string | null,
|
||||
'hcaptcha-response'?: string | null,
|
||||
} => {
|
||||
@@ -144,10 +154,10 @@ describe('2要素認証', () => {
|
||||
]);
|
||||
const clientDataJSONBuffer = Buffer.from(JSON.stringify({
|
||||
type: 'webauthn.get',
|
||||
challenge: param.challenge,
|
||||
challenge: param.requestOptions.challenge,
|
||||
origin: config.scheme + '://' + config.host,
|
||||
androidPackageName: 'org.mozilla.firefox',
|
||||
}));
|
||||
}), 'utf-8');
|
||||
const hashedclientDataJSON = crypto.createHash('sha256')
|
||||
.update(clientDataJSONBuffer)
|
||||
.digest();
|
||||
@@ -156,13 +166,19 @@ describe('2要素認証', () => {
|
||||
.update(Buffer.concat([authenticatorData, hashedclientDataJSON]))
|
||||
.sign(privateKey);
|
||||
return {
|
||||
authenticatorData: authenticatorData.toString('hex'),
|
||||
credentialId: param.credentialId.toString('base64'),
|
||||
challengeId: param.challengeId,
|
||||
clientDataJSON: clientDataJSONBuffer.toString('hex'),
|
||||
username,
|
||||
password,
|
||||
signature: signature.toString('hex'),
|
||||
credential: <AuthenticationResponseJSON>{
|
||||
id: param.credentialId.toString('base64url'),
|
||||
rawId: param.credentialId.toString('base64url'),
|
||||
response: <AuthenticatorAssertionResponseJSON>{
|
||||
clientDataJSON: clientDataJSONBuffer.toString('base64url'),
|
||||
authenticatorData: authenticatorData.toString('base64url'),
|
||||
signature: signature.toString('base64url'),
|
||||
},
|
||||
clientExtensionResults: {},
|
||||
type: 'public-key',
|
||||
},
|
||||
'g-recaptcha-response': null,
|
||||
'hcaptcha-response': null,
|
||||
};
|
||||
@@ -191,7 +207,7 @@ describe('2要素認証', () => {
|
||||
const doneResponse = await api('/i/2fa/done', {
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
}, alice);
|
||||
assert.strictEqual(doneResponse.status, 204);
|
||||
assert.strictEqual(doneResponse.status, 200);
|
||||
|
||||
const usersShowResponse = await api('/users/show', {
|
||||
username,
|
||||
@@ -205,6 +221,12 @@ describe('2要素認証', () => {
|
||||
});
|
||||
assert.strictEqual(signinResponse.status, 200);
|
||||
assert.notEqual(signinResponse.body.i, undefined);
|
||||
|
||||
// 後片付け
|
||||
await api('/i/2fa/unregister', {
|
||||
password,
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
}, alice);
|
||||
});
|
||||
|
||||
test('が設定でき、セキュリティキーでログインできる。', async () => {
|
||||
@@ -216,25 +238,26 @@ describe('2要素認証', () => {
|
||||
const doneResponse = await api('/i/2fa/done', {
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
}, alice);
|
||||
assert.strictEqual(doneResponse.status, 204);
|
||||
assert.strictEqual(doneResponse.status, 200);
|
||||
|
||||
const registerKeyResponse = await api('/i/2fa/register-key', {
|
||||
password,
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
}, alice);
|
||||
assert.strictEqual(registerKeyResponse.status, 200);
|
||||
assert.notEqual(registerKeyResponse.body.challengeId, undefined);
|
||||
assert.notEqual(registerKeyResponse.body.rp, undefined);
|
||||
assert.notEqual(registerKeyResponse.body.challenge, undefined);
|
||||
|
||||
const keyName = 'example-key';
|
||||
const credentialId = crypto.randomBytes(0x41);
|
||||
const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
keyName,
|
||||
challengeId: registerKeyResponse.body.challengeId,
|
||||
challenge: registerKeyResponse.body.challenge,
|
||||
credentialId,
|
||||
creationOptions: registerKeyResponse.body,
|
||||
}), alice);
|
||||
assert.strictEqual(keyDoneResponse.status, 200);
|
||||
assert.strictEqual(keyDoneResponse.body.id, credentialId.toString('hex'));
|
||||
assert.strictEqual(keyDoneResponse.body.id, credentialId.toString('base64url'));
|
||||
assert.strictEqual(keyDoneResponse.body.name, keyName);
|
||||
|
||||
const usersShowResponse = await api('/users/show', {
|
||||
@@ -248,19 +271,23 @@ describe('2要素認証', () => {
|
||||
});
|
||||
assert.strictEqual(signinResponse.status, 200);
|
||||
assert.strictEqual(signinResponse.body.i, undefined);
|
||||
assert.notEqual(signinResponse.body.challengeId, undefined);
|
||||
assert.notEqual(signinResponse.body.challenge, undefined);
|
||||
assert.notEqual(signinResponse.body.securityKeys, undefined);
|
||||
assert.strictEqual(signinResponse.body.securityKeys[0].id, credentialId.toString('hex'));
|
||||
assert.notEqual(signinResponse.body.allowCredentials, undefined);
|
||||
assert.strictEqual(signinResponse.body.allowCredentials[0].id, credentialId.toString('base64url'));
|
||||
|
||||
const signinResponse2 = await api('/signin', signinWithSecurityKeyParam({
|
||||
keyName,
|
||||
challengeId: signinResponse.body.challengeId,
|
||||
challenge: signinResponse.body.challenge,
|
||||
credentialId,
|
||||
requestOptions: signinResponse.body,
|
||||
}));
|
||||
assert.strictEqual(signinResponse2.status, 200);
|
||||
assert.notEqual(signinResponse2.body.i, undefined);
|
||||
|
||||
// 後片付け
|
||||
await api('/i/2fa/unregister', {
|
||||
password,
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
}, alice);
|
||||
});
|
||||
|
||||
test('が設定でき、セキュリティキーでパスワードレスログインできる。', async () => {
|
||||
@@ -272,9 +299,10 @@ describe('2要素認証', () => {
|
||||
const doneResponse = await api('/i/2fa/done', {
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
}, alice);
|
||||
assert.strictEqual(doneResponse.status, 204);
|
||||
assert.strictEqual(doneResponse.status, 200);
|
||||
|
||||
const registerKeyResponse = await api('/i/2fa/register-key', {
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
password,
|
||||
}, alice);
|
||||
assert.strictEqual(registerKeyResponse.status, 200);
|
||||
@@ -282,10 +310,10 @@ describe('2要素認証', () => {
|
||||
const keyName = 'example-key';
|
||||
const credentialId = crypto.randomBytes(0x41);
|
||||
const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
keyName,
|
||||
challengeId: registerKeyResponse.body.challengeId,
|
||||
challenge: registerKeyResponse.body.challenge,
|
||||
credentialId,
|
||||
creationOptions: registerKeyResponse.body,
|
||||
}), alice);
|
||||
assert.strictEqual(keyDoneResponse.status, 200);
|
||||
|
||||
@@ -310,14 +338,19 @@ describe('2要素認証', () => {
|
||||
const signinResponse2 = await api('/signin', {
|
||||
...signinWithSecurityKeyParam({
|
||||
keyName,
|
||||
challengeId: signinResponse.body.challengeId,
|
||||
challenge: signinResponse.body.challenge,
|
||||
credentialId,
|
||||
requestOptions: signinResponse.body,
|
||||
}),
|
||||
password: '',
|
||||
});
|
||||
assert.strictEqual(signinResponse2.status, 200);
|
||||
assert.notEqual(signinResponse2.body.i, undefined);
|
||||
|
||||
// 後片付け
|
||||
await api('/i/2fa/unregister', {
|
||||
password,
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
}, alice);
|
||||
});
|
||||
|
||||
test('が設定でき、設定したセキュリティキーの名前を変更できる。', async () => {
|
||||
@@ -329,9 +362,10 @@ describe('2要素認証', () => {
|
||||
const doneResponse = await api('/i/2fa/done', {
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
}, alice);
|
||||
assert.strictEqual(doneResponse.status, 204);
|
||||
assert.strictEqual(doneResponse.status, 200);
|
||||
|
||||
const registerKeyResponse = await api('/i/2fa/register-key', {
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
password,
|
||||
}, alice);
|
||||
assert.strictEqual(registerKeyResponse.status, 200);
|
||||
@@ -339,27 +373,33 @@ describe('2要素認証', () => {
|
||||
const keyName = 'example-key';
|
||||
const credentialId = crypto.randomBytes(0x41);
|
||||
const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
keyName,
|
||||
challengeId: registerKeyResponse.body.challengeId,
|
||||
challenge: registerKeyResponse.body.challenge,
|
||||
credentialId,
|
||||
creationOptions: registerKeyResponse.body,
|
||||
}), alice);
|
||||
assert.strictEqual(keyDoneResponse.status, 200);
|
||||
|
||||
const renamedKey = 'other-key';
|
||||
const updateKeyResponse = await api('/i/2fa/update-key', {
|
||||
name: renamedKey,
|
||||
credentialId: credentialId.toString('hex'),
|
||||
credentialId: credentialId.toString('base64url'),
|
||||
}, alice);
|
||||
assert.strictEqual(updateKeyResponse.status, 200);
|
||||
|
||||
const iResponse = await api('/i', {
|
||||
}, alice);
|
||||
assert.strictEqual(iResponse.status, 200);
|
||||
const securityKeys = iResponse.body.securityKeysList.filter(s => s.id === credentialId.toString('hex'));
|
||||
const securityKeys = iResponse.body.securityKeysList.filter((s: { id: string; }) => s.id === credentialId.toString('base64url'));
|
||||
assert.strictEqual(securityKeys.length, 1);
|
||||
assert.strictEqual(securityKeys[0].name, renamedKey);
|
||||
assert.notEqual(securityKeys[0].lastUsed, undefined);
|
||||
|
||||
// 後片付け
|
||||
await api('/i/2fa/unregister', {
|
||||
password,
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
}, alice);
|
||||
});
|
||||
|
||||
test('が設定でき、設定したセキュリティキーを削除できる。', async () => {
|
||||
@@ -371,9 +411,10 @@ describe('2要素認証', () => {
|
||||
const doneResponse = await api('/i/2fa/done', {
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
}, alice);
|
||||
assert.strictEqual(doneResponse.status, 204);
|
||||
assert.strictEqual(doneResponse.status, 200);
|
||||
|
||||
const registerKeyResponse = await api('/i/2fa/register-key', {
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
password,
|
||||
}, alice);
|
||||
assert.strictEqual(registerKeyResponse.status, 200);
|
||||
@@ -381,10 +422,10 @@ describe('2要素認証', () => {
|
||||
const keyName = 'example-key';
|
||||
const credentialId = crypto.randomBytes(0x41);
|
||||
const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
keyName,
|
||||
challengeId: registerKeyResponse.body.challengeId,
|
||||
challenge: registerKeyResponse.body.challenge,
|
||||
credentialId,
|
||||
creationOptions: registerKeyResponse.body,
|
||||
}), alice);
|
||||
assert.strictEqual(keyDoneResponse.status, 200);
|
||||
|
||||
@@ -394,6 +435,7 @@ describe('2要素認証', () => {
|
||||
assert.strictEqual(iResponse.status, 200);
|
||||
for (const key of iResponse.body.securityKeysList) {
|
||||
const removeKeyResponse = await api('/i/2fa/remove-key', {
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
password,
|
||||
credentialId: key.id,
|
||||
}, alice);
|
||||
@@ -412,6 +454,12 @@ describe('2要素認証', () => {
|
||||
});
|
||||
assert.strictEqual(signinResponse.status, 200);
|
||||
assert.notEqual(signinResponse.body.i, undefined);
|
||||
|
||||
// 後片付け
|
||||
await api('/i/2fa/unregister', {
|
||||
password,
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
}, alice);
|
||||
});
|
||||
|
||||
test('が設定でき、設定解除できる。(パスワードのみでログインできる。)', async () => {
|
||||
@@ -423,7 +471,7 @@ describe('2要素認証', () => {
|
||||
const doneResponse = await api('/i/2fa/done', {
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
}, alice);
|
||||
assert.strictEqual(doneResponse.status, 204);
|
||||
assert.strictEqual(doneResponse.status, 200);
|
||||
|
||||
const usersShowResponse = await api('/users/show', {
|
||||
username,
|
||||
@@ -432,6 +480,7 @@ describe('2要素認証', () => {
|
||||
assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true);
|
||||
|
||||
const unregisterResponse = await api('/i/2fa/unregister', {
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
password,
|
||||
}, alice);
|
||||
assert.strictEqual(unregisterResponse.status, 204);
|
||||
@@ -441,5 +490,11 @@ describe('2要素認証', () => {
|
||||
});
|
||||
assert.strictEqual(signinResponse.status, 200);
|
||||
assert.notEqual(signinResponse.body.i, undefined);
|
||||
|
||||
// 後片付け
|
||||
await api('/i/2fa/unregister', {
|
||||
password,
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
}, alice);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -721,7 +721,7 @@ describe('クリップ', () => {
|
||||
await addNote({ clipId: aliceClip.id, noteId: aliceNote.id });
|
||||
const res = await show({ clipId: aliceClip.id });
|
||||
assert.strictEqual(res.lastClippedAt, new Date(res.lastClippedAt ?? '').toISOString());
|
||||
assert.deepStrictEqual(await notes({ clipId: aliceClip.id }), [aliceNote]);
|
||||
assert.deepStrictEqual((await notes({ clipId: aliceClip.id })).map(x => x.id), [aliceNote.id]);
|
||||
|
||||
// 他人の非公開ノートも突っ込める
|
||||
await addNote({ clipId: aliceClip.id, noteId: bobHomeNote.id });
|
||||
@@ -861,8 +861,8 @@ describe('クリップ', () => {
|
||||
bobNote, bobHomeNote,
|
||||
];
|
||||
assert.deepStrictEqual(
|
||||
res.sort(compareBy(s => s.id)),
|
||||
expects.sort(compareBy(s => s.id)));
|
||||
res.sort(compareBy(s => s.id)).map(x => x.id),
|
||||
expects.sort(compareBy(s => s.id)).map(x => x.id));
|
||||
});
|
||||
|
||||
test('を始端IDとlimitで取得できる。', async () => {
|
||||
@@ -881,8 +881,8 @@ describe('クリップ', () => {
|
||||
// Promise.allで返ってくる配列はID順で並んでないのでソートして厳密比較
|
||||
const expects = [noteList[3], noteList[4], noteList[5]];
|
||||
assert.deepStrictEqual(
|
||||
res.sort(compareBy(s => s.id)),
|
||||
expects.sort(compareBy(s => s.id)));
|
||||
res.sort(compareBy(s => s.id)).map(x => x.id),
|
||||
expects.sort(compareBy(s => s.id)).map(x => x.id));
|
||||
});
|
||||
|
||||
test('をID範囲指定で取得できる。', async () => {
|
||||
@@ -901,8 +901,8 @@ describe('クリップ', () => {
|
||||
// Promise.allで返ってくる配列はID順で並んでないのでソートして厳密比較
|
||||
const expects = [noteList[2], noteList[3]];
|
||||
assert.deepStrictEqual(
|
||||
res.sort(compareBy(s => s.id)),
|
||||
expects.sort(compareBy(s => s.id)));
|
||||
res.sort(compareBy(s => s.id)).map(x => x.id),
|
||||
expects.sort(compareBy(s => s.id)).map(x => x.id));
|
||||
});
|
||||
|
||||
test.todo('Remoteのノートもクリップできる。どうテストしよう?');
|
||||
@@ -911,7 +911,7 @@ describe('クリップ', () => {
|
||||
const bobClip = await create({ isPublic: true }, { user: bob } );
|
||||
await addNote({ clipId: bobClip.id, noteId: aliceNote.id }, { user: bob });
|
||||
const res = await notes({ clipId: bobClip.id });
|
||||
assert.deepStrictEqual(res, [aliceNote]);
|
||||
assert.deepStrictEqual(res.map(x => x.id), [aliceNote.id]);
|
||||
});
|
||||
|
||||
test('はPublicなクリップなら認証なしでも取得できる。(非公開ノートはhideされて返ってくる)', async () => {
|
||||
@@ -928,8 +928,8 @@ describe('クリップ', () => {
|
||||
hiddenNote(aliceFollowersNote), hiddenNote(aliceSpecifiedNote),
|
||||
];
|
||||
assert.deepStrictEqual(
|
||||
res.sort(compareBy(s => s.id)),
|
||||
expects.sort(compareBy(s => s.id)));
|
||||
res.sort(compareBy(s => s.id)).map(x => x.id),
|
||||
expects.sort(compareBy(s => s.id)).map(x => x.id));
|
||||
});
|
||||
|
||||
test.todo('ブロック、ミュートされたユーザーからの設定&取得etc.');
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as assert from 'assert';
|
||||
// node-fetch only supports it's own Blob yet
|
||||
// https://github.com/node-fetch/node-fetch/pull/1664
|
||||
import { Blob } from 'node-fetch';
|
||||
import { User } from '@/models/index.js';
|
||||
import { MiUser } from '@/models/_.js';
|
||||
import { startServer, signup, post, api, uploadFile, simpleGet, initTestDb } from '../utils.js';
|
||||
import type { INestApplicationContext } from '@nestjs/common';
|
||||
import type * as misskey from 'misskey-js';
|
||||
@@ -298,7 +298,7 @@ describe('Endpoints', () => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
|
||||
const connection = await initTestDb(true);
|
||||
const Users = connection.getRepository(User);
|
||||
const Users = connection.getRepository(MiUser);
|
||||
const newBob = await Users.findOneByOrFail({ id: bob.id });
|
||||
assert.strictEqual(newBob.followersCount, 0);
|
||||
assert.strictEqual(newBob.followingCount, 1);
|
||||
@@ -360,7 +360,7 @@ describe('Endpoints', () => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
|
||||
const connection = await initTestDb(true);
|
||||
const Users = connection.getRepository(User);
|
||||
const Users = connection.getRepository(MiUser);
|
||||
const newBob = await Users.findOneByOrFail({ id: bob.id });
|
||||
assert.strictEqual(newBob.followersCount, 0);
|
||||
assert.strictEqual(newBob.followingCount, 0);
|
||||
|
||||
@@ -34,6 +34,8 @@ describe('Webリソース', () => {
|
||||
let aliceGalleryPost: any;
|
||||
let aliceChannel: any;
|
||||
|
||||
let bob: misskey.entities.MeSignup;
|
||||
|
||||
type Request = {
|
||||
path: string,
|
||||
accept?: string,
|
||||
@@ -90,6 +92,8 @@ describe('Webリソース', () => {
|
||||
fileIds: [aliceUploadedFile.body.id],
|
||||
});
|
||||
aliceChannel = await channel(alice, {});
|
||||
|
||||
bob = await signup({ username: 'alice' });
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -163,9 +167,15 @@ describe('Webリソース', () => {
|
||||
});
|
||||
|
||||
describe.each([{ path: '/queue' }])('$path', ({ path }) => {
|
||||
test('はログインしないとGETできない。', async () => await notOk({
|
||||
path,
|
||||
status: 401,
|
||||
}));
|
||||
|
||||
test('はadminでなければGETできない。', async () => await notOk({
|
||||
path,
|
||||
status: 500, // FIXME? 403ではない。
|
||||
cookie: cookie(bob),
|
||||
status: 403,
|
||||
}));
|
||||
|
||||
test('はadminならGETできる。', async () => await ok({
|
||||
|
||||
@@ -7,7 +7,7 @@ process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { loadConfig } from '@/config.js';
|
||||
import { User, UsersRepository } from '@/models/index.js';
|
||||
import { MiUser, UsersRepository } from '@/models/_.js';
|
||||
import { jobQueue } from '@/boot/common.js';
|
||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||
import { uploadFile, signup, startServer, initTestDb, api, sleep, successfulApiCall } from '../utils.js';
|
||||
@@ -42,7 +42,7 @@ describe('Account Move', () => {
|
||||
dave = await signup({ username: 'dave' });
|
||||
eve = await signup({ username: 'eve' });
|
||||
frank = await signup({ username: 'frank' });
|
||||
Users = connection.getRepository(User);
|
||||
Users = connection.getRepository(MiUser);
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { Note } from '@/models/entities/Note.js';
|
||||
import { MiNote } from '@/models/Note.js';
|
||||
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
|
||||
import { signup, post, uploadUrl, startServer, initTestDb, api, uploadFile } from '../utils.js';
|
||||
import type { INestApplicationContext } from '@nestjs/common';
|
||||
@@ -22,7 +22,7 @@ describe('Note', () => {
|
||||
beforeAll(async () => {
|
||||
app = await startServer();
|
||||
const connection = await initTestDb(true);
|
||||
Notes = connection.getRepository(Note);
|
||||
Notes = connection.getRepository(MiNote);
|
||||
alice = await signup({ username: 'alice' });
|
||||
bob = await signup({ username: 'bob' });
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { Following } from '@/models/entities/Following.js';
|
||||
import { MiFollowing } from '@/models/Following.js';
|
||||
import { connectStream, signup, api, post, startServer, initTestDb, waitFire } from '../utils.js';
|
||||
import type { INestApplicationContext } from '@nestjs/common';
|
||||
import type * as misskey from 'misskey-js';
|
||||
@@ -46,7 +46,7 @@ describe('Streaming', () => {
|
||||
beforeAll(async () => {
|
||||
app = await startServer();
|
||||
const connection = await initTestDb(true);
|
||||
Followings = connection.getRepository(Following);
|
||||
Followings = connection.getRepository(MiFollowing);
|
||||
|
||||
ayano = await signup({ username: 'ayano' });
|
||||
kyoko = await signup({ username: 'kyoko' });
|
||||
|
||||
@@ -102,6 +102,7 @@ describe('ユーザー', () => {
|
||||
birthday: user.birthday,
|
||||
lang: user.lang,
|
||||
fields: user.fields,
|
||||
verifiedLinks: user.verifiedLinks,
|
||||
followersCount: user.followersCount,
|
||||
followingCount: user.followingCount,
|
||||
notesCount: user.notesCount,
|
||||
@@ -131,6 +132,7 @@ describe('ユーザー', () => {
|
||||
isBlocked: user.isBlocked ?? false,
|
||||
isMuted: user.isMuted ?? false,
|
||||
isRenoteMuted: user.isRenoteMuted ?? false,
|
||||
notify: user.notify ?? 'none',
|
||||
});
|
||||
};
|
||||
|
||||
@@ -152,6 +154,7 @@ describe('ユーザー', () => {
|
||||
preventAiLearning: user.preventAiLearning,
|
||||
isExplorable: user.isExplorable,
|
||||
isDeleted: user.isDeleted,
|
||||
twoFactorBackupCodesStock: user.twoFactorBackupCodesStock,
|
||||
hideOnlineStatus: user.hideOnlineStatus,
|
||||
hasUnreadSpecifiedNotes: user.hasUnreadSpecifiedNotes,
|
||||
hasUnreadMentions: user.hasUnreadMentions,
|
||||
@@ -368,6 +371,7 @@ describe('ユーザー', () => {
|
||||
assert.strictEqual(response.birthday, null);
|
||||
assert.strictEqual(response.lang, null);
|
||||
assert.deepStrictEqual(response.fields, []);
|
||||
assert.deepStrictEqual(response.verifiedLinks, []);
|
||||
assert.strictEqual(response.followersCount, 0);
|
||||
assert.strictEqual(response.followingCount, 0);
|
||||
assert.strictEqual(response.notesCount, 0);
|
||||
@@ -398,6 +402,7 @@ describe('ユーザー', () => {
|
||||
assert.strictEqual(response.preventAiLearning, true);
|
||||
assert.strictEqual(response.isExplorable, true);
|
||||
assert.strictEqual(response.isDeleted, false);
|
||||
assert.strictEqual(response.twoFactorBackupCodesStock, 'none');
|
||||
assert.strictEqual(response.hideOnlineStatus, false);
|
||||
assert.strictEqual(response.hasUnreadSpecifiedNotes, false);
|
||||
assert.strictEqual(response.hasUnreadMentions, false);
|
||||
@@ -490,7 +495,7 @@ describe('ユーザー', () => {
|
||||
{ parameters: (): object => ({ mutedWords: [] }) },
|
||||
{ parameters: (): object => ({ mutedInstances: ['xxxx.xxxxx'] }) },
|
||||
{ parameters: (): object => ({ mutedInstances: [] }) },
|
||||
{ parameters: (): object => ({ mutingNotificationTypes: ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] }) },
|
||||
{ parameters: (): object => ({ mutingNotificationTypes: ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] }) },
|
||||
{ parameters: (): object => ({ mutingNotificationTypes: [] }) },
|
||||
{ parameters: (): object => ({ emailNotificationTypes: ['mention', 'reply', 'quote', 'follow', 'receiveFollowRequest'] }) },
|
||||
{ parameters: (): object => ({ emailNotificationTypes: [] }) },
|
||||
|
||||
@@ -15,7 +15,7 @@ import type { LoggerService } from '@/core/LoggerService.js';
|
||||
import type { MetaService } from '@/core/MetaService.js';
|
||||
import type { UtilityService } from '@/core/UtilityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { NoteReactionsRepository, NotesRepository, PollsRepository, UsersRepository } from '@/models/index.js';
|
||||
import type { NoteReactionsRepository, NotesRepository, PollsRepository, UsersRepository } from '@/models/_.js';
|
||||
|
||||
type MockResponse = {
|
||||
type: string;
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
"declaration": false,
|
||||
"sourceMap": true,
|
||||
"target": "ES2022",
|
||||
"module": "es2020",
|
||||
"moduleResolution": "node16",
|
||||
"module": "nodenext",
|
||||
"moduleResolution": "nodenext",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"removeComments": false,
|
||||
"noLib": false,
|
||||
|
||||
@@ -10,9 +10,9 @@ import { ModuleMocker } from 'jest-mock';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { AnnouncementService } from '@/core/AnnouncementService.js';
|
||||
import type { Announcement, AnnouncementsRepository, AnnouncementReadsRepository, UsersRepository, User } from '@/models/index.js';
|
||||
import type { MiAnnouncement, AnnouncementsRepository, AnnouncementReadsRepository, UsersRepository, MiUser } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { genAid } from '@/misc/id/aid.js';
|
||||
import { genAidx } from '@/misc/id/aidx.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
@@ -30,10 +30,10 @@ describe('AnnouncementService', () => {
|
||||
let announcementReadsRepository: AnnouncementReadsRepository;
|
||||
let globalEventService: jest.Mocked<GlobalEventService>;
|
||||
|
||||
function createUser(data: Partial<User> = {}) {
|
||||
function createUser(data: Partial<MiUser> = {}) {
|
||||
const un = secureRndstr(16);
|
||||
return usersRepository.insert({
|
||||
id: genAid(new Date()),
|
||||
id: genAidx(new Date()),
|
||||
createdAt: new Date(),
|
||||
username: un,
|
||||
usernameLower: un,
|
||||
@@ -42,9 +42,9 @@ describe('AnnouncementService', () => {
|
||||
.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
|
||||
}
|
||||
|
||||
function createAnnouncement(data: Partial<Announcement> = {}) {
|
||||
function createAnnouncement(data: Partial<MiAnnouncement> = {}) {
|
||||
return announcementsRepository.insert({
|
||||
id: genAid(new Date()),
|
||||
id: genAidx(new Date()),
|
||||
createdAt: new Date(),
|
||||
updatedAt: null,
|
||||
title: 'Title',
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import { jest } from '@jest/globals';
|
||||
import { ModuleMocker } from 'jest-mock';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { Redis } from 'ioredis';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
@@ -18,7 +17,6 @@ import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { TestingModule } from '@nestjs/testing';
|
||||
import type { MockFunctionMetadata } from 'jest-mock';
|
||||
|
||||
function mockRedis() {
|
||||
const hash = {};
|
||||
@@ -35,9 +33,9 @@ describe('FetchInstanceMetadataService', () => {
|
||||
let fetchInstanceMetadataService: jest.Mocked<FetchInstanceMetadataService>;
|
||||
let federatedInstanceService: jest.Mocked<FederatedInstanceService>;
|
||||
let httpRequestService: jest.Mocked<HttpRequestService>;
|
||||
let redisClient: jest.Mocked<Redis.Redis>;
|
||||
let redisClient: jest.Mocked<Redis>;
|
||||
|
||||
beforeAll(async () => {
|
||||
beforeEach(async () => {
|
||||
app = await Test
|
||||
.createTestingModule({
|
||||
imports: [
|
||||
@@ -64,11 +62,11 @@ describe('FetchInstanceMetadataService', () => {
|
||||
|
||||
fetchInstanceMetadataService = app.get<FetchInstanceMetadataService>(FetchInstanceMetadataService);
|
||||
federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService) as jest.Mocked<FederatedInstanceService>;
|
||||
redisClient = app.get<Redis.Redis>(DI.redis) as jest.Mocked<Redis.Redis>;
|
||||
redisClient = app.get<Redis>(DI.redis) as jest.Mocked<Redis>;
|
||||
httpRequestService = app.get<HttpRequestService>(HttpRequestService) as jest.Mocked<HttpRequestService>;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -85,6 +83,7 @@ describe('FetchInstanceMetadataService', () => {
|
||||
expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
|
||||
expect(httpRequestService.getJson).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Lock and don\'t update', async () => {
|
||||
redisClient.set = mockRedis();
|
||||
const now = Date.now();
|
||||
@@ -98,6 +97,7 @@ describe('FetchInstanceMetadataService', () => {
|
||||
expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
|
||||
expect(httpRequestService.getJson).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('Do nothing when lock not acquired', async () => {
|
||||
redisClient.set = mockRedis();
|
||||
federatedInstanceService.fetch.mockReturnValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } });
|
||||
|
||||
@@ -9,7 +9,7 @@ import { jest } from '@jest/globals';
|
||||
import { ModuleMocker } from 'jest-mock';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import type { MetasRepository } from '@/models/index.js';
|
||||
import type { MetasRepository } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { CoreModule } from '@/core/CoreModule.js';
|
||||
|
||||
@@ -15,7 +15,7 @@ import { CreateSystemUserService } from '@/core/CreateSystemUserService.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { QueueService } from '@/core/QueueService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import type { RelaysRepository } from '@/models/index.js';
|
||||
import type { RelaysRepository } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { TestingModule } from '@nestjs/testing';
|
||||
import type { MockFunctionMetadata } from 'jest-mock';
|
||||
|
||||
@@ -11,10 +11,10 @@ import { Test } from '@nestjs/testing';
|
||||
import * as lolex from '@sinonjs/fake-timers';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import type { Role, RolesRepository, RoleAssignmentsRepository, UsersRepository, User } from '@/models/index.js';
|
||||
import type { MiRole, RolesRepository, RoleAssignmentsRepository, UsersRepository, MiUser } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { genAid } from '@/misc/id/aid.js';
|
||||
import { genAidx } from '@/misc/id/aidx.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
@@ -34,10 +34,10 @@ describe('RoleService', () => {
|
||||
let metaService: jest.Mocked<MetaService>;
|
||||
let clock: lolex.InstalledClock;
|
||||
|
||||
function createUser(data: Partial<User> = {}) {
|
||||
function createUser(data: Partial<MiUser> = {}) {
|
||||
const un = secureRndstr(16);
|
||||
return usersRepository.insert({
|
||||
id: genAid(new Date()),
|
||||
id: genAidx(new Date()),
|
||||
createdAt: new Date(),
|
||||
username: un,
|
||||
usernameLower: un,
|
||||
@@ -46,9 +46,9 @@ describe('RoleService', () => {
|
||||
.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
|
||||
}
|
||||
|
||||
function createRole(data: Partial<Role> = {}) {
|
||||
function createRole(data: Partial<MiRole> = {}) {
|
||||
return rolesRepository.insert({
|
||||
id: genAid(new Date()),
|
||||
id: genAidx(new Date()),
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
lastUsedAt: new Date(),
|
||||
@@ -204,7 +204,7 @@ describe('RoleService', () => {
|
||||
createdAt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 365)),
|
||||
followersCount: 10,
|
||||
});
|
||||
const role = await createRole({
|
||||
await createRole({
|
||||
name: 'a',
|
||||
policies: {
|
||||
canManageCustomEmojis: {
|
||||
|
||||
@@ -10,8 +10,8 @@ import { UploadPartCommand, CompleteMultipartUploadCommand, CreateMultipartUploa
|
||||
import { mockClient } from 'aws-sdk-client-mock';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { CoreModule } from '@/core/CoreModule.js';
|
||||
import { S3Service } from '@/core/S3Service';
|
||||
import { Meta } from '@/models';
|
||||
import { S3Service } from '@/core/S3Service.js';
|
||||
import { MiMeta } from '@/models/_.js';
|
||||
import type { TestingModule } from '@nestjs/testing';
|
||||
|
||||
describe('S3Service', () => {
|
||||
@@ -40,7 +40,7 @@ describe('S3Service', () => {
|
||||
test('upload a file', async () => {
|
||||
s3Mock.on(PutObjectCommand).resolves({});
|
||||
|
||||
await s3Service.upload({ objectStorageRegion: 'us-east-1' } as Meta, {
|
||||
await s3Service.upload({ objectStorageRegion: 'us-east-1' } as MiMeta, {
|
||||
Bucket: 'fake',
|
||||
Key: 'fake',
|
||||
Body: 'x',
|
||||
@@ -52,7 +52,7 @@ describe('S3Service', () => {
|
||||
s3Mock.on(UploadPartCommand).resolves({ ETag: '1' });
|
||||
s3Mock.on(CompleteMultipartUploadCommand).resolves({ Bucket: 'fake', Key: 'fake' });
|
||||
|
||||
await s3Service.upload({} as Meta, {
|
||||
await s3Service.upload({} as MiMeta, {
|
||||
Bucket: 'fake',
|
||||
Key: 'fake',
|
||||
Body: 'x'.repeat(8 * 1024 * 1024 + 1), // デフォルトpartSizeにしている 8 * 1024 * 1024 を越えるサイズ
|
||||
@@ -62,7 +62,7 @@ describe('S3Service', () => {
|
||||
test('upload a file error', async () => {
|
||||
s3Mock.on(PutObjectCommand).rejects({ name: 'Fake Error' });
|
||||
|
||||
await expect(s3Service.upload({ objectStorageRegion: 'us-east-1' } as Meta, {
|
||||
await expect(s3Service.upload({ objectStorageRegion: 'us-east-1' } as MiMeta, {
|
||||
Bucket: 'fake',
|
||||
Key: 'fake',
|
||||
Body: 'x',
|
||||
@@ -72,7 +72,7 @@ describe('S3Service', () => {
|
||||
test('upload a large file error', async () => {
|
||||
s3Mock.on(UploadPartCommand).rejects();
|
||||
|
||||
await expect(s3Service.upload({} as Meta, {
|
||||
await expect(s3Service.upload({} as MiMeta, {
|
||||
Bucket: 'fake',
|
||||
Key: 'fake',
|
||||
Body: 'x'.repeat(8 * 1024 * 1024 + 1), // デフォルトpartSizeにしている 8 * 1024 * 1024 を越えるサイズ
|
||||
|
||||
@@ -18,11 +18,11 @@ import { CoreModule } from '@/core/CoreModule.js';
|
||||
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import type { IActor, IApDocument, ICollection, IPost } from '@/core/activitypub/type.js';
|
||||
import { Meta, Note } from '@/models/index.js';
|
||||
import { MiMeta, MiNote } from '@/models/_.js';
|
||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||
import { DownloadService } from '@/core/DownloadService.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import type { RemoteUser } from '@/models/entities/User.js';
|
||||
import type { MiRemoteUser } from '@/models/User.js';
|
||||
import { MockResolver } from '../misc/mock-resolver.js';
|
||||
|
||||
const host = 'https://host1.test';
|
||||
@@ -75,7 +75,7 @@ function createRandomFeaturedCollection(actor: NonTransientIActor, length: numbe
|
||||
async function createRandomRemoteUser(
|
||||
resolver: MockResolver,
|
||||
personService: ApPersonService,
|
||||
): Promise<RemoteUser> {
|
||||
): Promise<MiRemoteUser> {
|
||||
const actor = createRandomActor();
|
||||
resolver.register(actor.id, actor);
|
||||
|
||||
@@ -94,7 +94,7 @@ describe('ActivityPub', () => {
|
||||
cacheRemoteSensitiveFiles: true,
|
||||
blockedHosts: [] as string[],
|
||||
sensitiveWords: [] as string[],
|
||||
} as Meta;
|
||||
} as MiMeta;
|
||||
let meta = metaInitial;
|
||||
|
||||
beforeAll(async () => {
|
||||
@@ -109,7 +109,7 @@ describe('ActivityPub', () => {
|
||||
},
|
||||
})
|
||||
.overrideProvider(MetaService).useValue({
|
||||
async fetch(): Promise<Meta> {
|
||||
async fetch(): Promise<MiMeta> {
|
||||
return meta;
|
||||
},
|
||||
}).compile();
|
||||
@@ -199,7 +199,7 @@ describe('ActivityPub', () => {
|
||||
rendererService.renderAnnounce(null, {
|
||||
createdAt: new Date(0),
|
||||
visibility: 'followers',
|
||||
} as Note);
|
||||
} as MiNote);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ 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';
|
||||
import type { AppLockService } from '@/core/AppLockService.js';
|
||||
import Logger from '@/logger.js';
|
||||
|
||||
describe('Chart', () => {
|
||||
|
||||
@@ -6,42 +6,42 @@
|
||||
import { correctFilename } from '@/misc/correct-filename.js';
|
||||
|
||||
describe(correctFilename, () => {
|
||||
it('no ext to null', () => {
|
||||
expect(correctFilename('test', null)).toBe('test.unknown');
|
||||
});
|
||||
it('no ext to jpg', () => {
|
||||
expect(correctFilename('test', 'jpg')).toBe('test.jpg');
|
||||
});
|
||||
it('jpg to webp', () => {
|
||||
expect(correctFilename('test.jpg', 'webp')).toBe('test.jpg.webp');
|
||||
});
|
||||
it('jpg to .webp', () => {
|
||||
expect(correctFilename('test.jpg', '.webp')).toBe('test.jpg.webp');
|
||||
});
|
||||
it('jpeg to jpg', () => {
|
||||
expect(correctFilename('test.jpeg', 'jpg')).toBe('test.jpeg');
|
||||
});
|
||||
it('JPEG to jpg', () => {
|
||||
expect(correctFilename('test.JPEG', 'jpg')).toBe('test.JPEG');
|
||||
});
|
||||
it('jpg to jpg', () => {
|
||||
expect(correctFilename('test.jpg', 'jpg')).toBe('test.jpg');
|
||||
});
|
||||
it('JPG to jpg', () => {
|
||||
expect(correctFilename('test.JPG', 'jpg')).toBe('test.JPG');
|
||||
});
|
||||
it('tiff to tif', () => {
|
||||
expect(correctFilename('test.tiff', 'tif')).toBe('test.tiff');
|
||||
});
|
||||
it('skip gz', () => {
|
||||
expect(correctFilename('test.unitypackage', 'gz')).toBe('test.unitypackage');
|
||||
});
|
||||
it('skip text file', () => {
|
||||
expect(correctFilename('test.txt', null)).toBe('test.txt');
|
||||
});
|
||||
it('unknown', () => {
|
||||
expect(correctFilename('test.hoge', null)).toBe('test.hoge');
|
||||
});
|
||||
it('no ext to null', () => {
|
||||
expect(correctFilename('test', null)).toBe('test.unknown');
|
||||
});
|
||||
it('no ext to jpg', () => {
|
||||
expect(correctFilename('test', 'jpg')).toBe('test.jpg');
|
||||
});
|
||||
it('jpg to webp', () => {
|
||||
expect(correctFilename('test.jpg', 'webp')).toBe('test.jpg.webp');
|
||||
});
|
||||
it('jpg to .webp', () => {
|
||||
expect(correctFilename('test.jpg', '.webp')).toBe('test.jpg.webp');
|
||||
});
|
||||
it('jpeg to jpg', () => {
|
||||
expect(correctFilename('test.jpeg', 'jpg')).toBe('test.jpeg');
|
||||
});
|
||||
it('JPEG to jpg', () => {
|
||||
expect(correctFilename('test.JPEG', 'jpg')).toBe('test.JPEG');
|
||||
});
|
||||
it('jpg to jpg', () => {
|
||||
expect(correctFilename('test.jpg', 'jpg')).toBe('test.jpg');
|
||||
});
|
||||
it('JPG to jpg', () => {
|
||||
expect(correctFilename('test.JPG', 'jpg')).toBe('test.JPG');
|
||||
});
|
||||
it('tiff to tif', () => {
|
||||
expect(correctFilename('test.tiff', 'tif')).toBe('test.tiff');
|
||||
});
|
||||
it('skip gz', () => {
|
||||
expect(correctFilename('test.unitypackage', 'gz')).toBe('test.unitypackage');
|
||||
});
|
||||
it('skip text file', () => {
|
||||
expect(correctFilename('test.txt', null)).toBe('test.txt');
|
||||
});
|
||||
it('unknown', () => {
|
||||
expect(correctFilename('test.hoge', null)).toBe('test.hoge');
|
||||
});
|
||||
test('non ascii with space', () => {
|
||||
expect(correctFilename('ファイル 名前', 'jpg')).toBe('ファイル 名前.jpg');
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { ulid } from 'ulid';
|
||||
import { describe, test, expect } from '@jest/globals';
|
||||
import { aidRegExp, genAid, parseAid } from '@/misc/id/aid.js';
|
||||
import { aidxRegExp, genAidx, parseAidx } from '@/misc/id/aidx.js';
|
||||
import { genMeid, meidRegExp, parseMeid } from '@/misc/id/meid.js';
|
||||
import { genMeidg, meidgRegExp, parseMeidg } from '@/misc/id/meidg.js';
|
||||
import { genObjectId, objectIdRegExp, parseObjectId } from '@/misc/id/object-id.js';
|
||||
@@ -19,6 +20,13 @@ describe('misc:id', () => {
|
||||
expect(parseAid(gotAid).date.getTime()).toBe(date.getTime());
|
||||
});
|
||||
|
||||
test('aidx', () => {
|
||||
const date = new Date();
|
||||
const gotAidx = genAidx(date);
|
||||
expect(gotAidx).toMatch(aidxRegExp);
|
||||
expect(parseAidx(gotAidx).date.getTime()).toBe(date.getTime());
|
||||
});
|
||||
|
||||
test('meid', () => {
|
||||
const date = new Date();
|
||||
const gotMeid = genMeid(date);
|
||||
|
||||
@@ -8,7 +8,7 @@ import { readFile } from 'node:fs/promises';
|
||||
import { isAbsolute, basename } from 'node:path';
|
||||
import { inspect } from 'node:util';
|
||||
import WebSocket, { ClientOptions } from 'ws';
|
||||
import fetch, { Blob, File, RequestInit } from 'node-fetch';
|
||||
import fetch, { File, RequestInit } from 'node-fetch';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { JSDOM } from 'jsdom';
|
||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||
|
||||
Reference in New Issue
Block a user