summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorおさむのひと <46447427+samunohito@users.noreply.github.com>2025-05-04 09:38:35 +0900
committerMarie <github@yuugi.dev>2025-05-08 20:03:53 +0200
commite40f3917f39da7e14cd11b89e6506862e7b92d11 (patch)
treeefe23e13f9f32f682216523aa2d8fc09e921c17f
parentRevert "fix: 添付ファイルのあるリクエストを受けたときの... (diff)
downloadsharkey-e40f3917f39da7e14cd11b89e6506862e7b92d11.tar.gz
sharkey-e40f3917f39da7e14cd11b89e6506862e7b92d11.tar.bz2
sharkey-e40f3917f39da7e14cd11b89e6506862e7b92d11.zip
refactor: ファイルアップロード時のテストを追加 (#15928)
* refactor: ファイルアップロード時のテストを追加 * なぜかsemverが消えてた
-rw-r--r--packages/backend/package.json4
-rw-r--r--packages/backend/src/server/ServerService.ts9
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files/create.ts1
-rw-r--r--packages/backend/test/unit/server/api/drive/files/create.ts164
-rw-r--r--pnpm-lock.yaml98
5 files changed, 274 insertions, 2 deletions
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 14ac79ae1b..b9cb0002ab 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -229,6 +229,7 @@
"@types/semver": "7.7.0",
"@types/simple-oauth2": "5.0.7",
"@types/sinonjs__fake-timers": "8.1.5",
+ "@types/supertest": "6.0.3",
"@types/tinycolor2": "1.4.6",
"@types/tmp": "0.2.6",
"@types/uuid": "^9.0.4",
@@ -246,6 +247,7 @@
"jest-mock": "29.7.0",
"nodemon": "3.1.10",
"pid-port": "1.0.2",
- "simple-oauth2": "5.1.0"
+ "simple-oauth2": "5.1.0",
+ "supertest": "7.1.0"
}
}
diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index dce47e2290..2d20aa1222 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -7,7 +7,7 @@ import cluster from 'node:cluster';
import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
-import Fastify, { FastifyInstance } from 'fastify';
+import Fastify, { type FastifyInstance } from 'fastify';
import fastifyStatic from '@fastify/static';
import fastifyRawBody from 'fastify-raw-body';
import { IsNull } from 'typeorm';
@@ -312,6 +312,13 @@ export class ServerService implements OnApplicationShutdown {
await this.#fastify.close();
}
+ /**
+ * Get the Fastify instance for testing.
+ */
+ public get fastify(): FastifyInstance {
+ return this.#fastify;
+ }
+
@bindThis
async onApplicationShutdown(signal: string): Promise<void> {
await this.dispose();
diff --git a/packages/backend/src/server/api/endpoints/drive/files/create.ts b/packages/backend/src/server/api/endpoints/drive/files/create.ts
index 7043f4883a..f4c47d71bf 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/create.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts
@@ -67,6 +67,7 @@ export const meta = {
message: 'Cannot upload the file because it exceeds the maximum file size.',
code: 'MAX_FILE_SIZE_EXCEEDED',
id: 'b9d8c348-33f0-4673-b9a9-5d4da058977a',
+ httpStatusCode: 413,
},
},
} as const;
diff --git a/packages/backend/test/unit/server/api/drive/files/create.ts b/packages/backend/test/unit/server/api/drive/files/create.ts
new file mode 100644
index 0000000000..9b38f4d744
--- /dev/null
+++ b/packages/backend/test/unit/server/api/drive/files/create.ts
@@ -0,0 +1,164 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Test, TestingModule } from '@nestjs/testing';
+import { FastifyInstance } from 'fastify';
+import request from 'supertest';
+import { randomString } from '../../../../../utils.js';
+import { CoreModule } from '@/core/CoreModule.js';
+import { RoleService } from '@/core/RoleService.js';
+import { DI } from '@/di-symbols.js';
+import { GlobalModule } from '@/GlobalModule.js';
+import { DriveFoldersRepository, MiDriveFolder, MiRole, UserProfilesRepository, UsersRepository } from '@/models/_.js';
+import { MiUser } from '@/models/User.js';
+import { ServerModule } from '@/server/ServerModule.js';
+import { ServerService } from '@/server/ServerService.js';
+import { IdService } from '@/core/IdService.js';
+
+describe('/drive/files/create', () => {
+ let module: TestingModule;
+ let server: FastifyInstance;
+ let roleService: RoleService;
+ let idService: IdService;
+
+ let root: MiUser;
+ let role_tinyAttachment: MiRole;
+
+ let folder: MiDriveFolder;
+
+ beforeAll(async () => {
+ module = await Test.createTestingModule({
+ imports: [GlobalModule, CoreModule, ServerModule],
+ }).compile();
+ module.enableShutdownHooks();
+
+ const serverService = module.get<ServerService>(ServerService);
+ await serverService.launch();
+ server = serverService.fastify;
+
+ idService = module.get(IdService);
+
+ const usersRepository = module.get<UsersRepository>(DI.usersRepository);
+ await usersRepository.delete({});
+ root = await usersRepository.insert({
+ id: idService.gen(),
+ username: 'root',
+ usernameLower: 'root',
+ token: '1234567890123456',
+ }).then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
+
+ const userProfilesRepository = module.get<UserProfilesRepository>(DI.userProfilesRepository);
+ await userProfilesRepository.delete({});
+ await userProfilesRepository.insert({
+ userId: root.id,
+ });
+
+ const driveFoldersRepository = module.get<DriveFoldersRepository>(DI.driveFoldersRepository);
+ folder = await driveFoldersRepository.insertOne({
+ id: idService.gen(),
+ name: 'root-folder',
+ parentId: null,
+ userId: root.id,
+ });
+
+ roleService = module.get<RoleService>(RoleService);
+ role_tinyAttachment = await roleService.create({
+ name: 'test-role001',
+ description: 'Test role001 description',
+ target: 'manual',
+ policies: {
+ maxFileSizeMb: {
+ useDefault: false,
+ priority: 1,
+ // 10byte
+ value: 10 / 1024 / 1024,
+ },
+ },
+ });
+ });
+
+ beforeEach(async () => {
+ await roleService.unassign(root.id, role_tinyAttachment.id).catch(() => {
+ });
+ });
+
+ afterAll(async () => {
+ await server.close();
+ await module.close();
+ });
+
+ async function postFile(props: {
+ name: string,
+ comment: string,
+ isSensitive: boolean,
+ force: boolean,
+ fileContent: Buffer | string,
+ }) {
+ const { name, comment, isSensitive, force, fileContent } = props;
+
+ return await request(server.server)
+ .post('/api/drive/files/create')
+ .set('Content-Type', 'multipart/form-data')
+ .attach('file', fileContent)
+ .field('name', name)
+ .field('comment', comment)
+ .field('isSensitive', isSensitive)
+ .field('force', force)
+ .field('folderId', folder.id)
+ .field('i', root.token ?? '');
+ }
+
+ test('200 ok', async () => {
+ const name = randomString();
+ const comment = randomString();
+ const result = await postFile({
+ name: name,
+ comment: comment,
+ isSensitive: true,
+ force: true,
+ fileContent: Buffer.from('a'.repeat(1000 * 1000)),
+ });
+ expect(result.statusCode).toBe(200);
+ expect(result.body.name).toBe(name + '.unknown');
+ expect(result.body.comment).toBe(comment);
+ expect(result.body.isSensitive).toBe(true);
+ expect(result.body.folderId).toBe(folder.id);
+ });
+
+ test('200 ok(with role)', async () => {
+ await roleService.assign(root.id, role_tinyAttachment.id);
+
+ const name = randomString();
+ const comment = randomString();
+ const result = await postFile({
+ name: name,
+ comment: comment,
+ isSensitive: true,
+ force: true,
+ fileContent: Buffer.from('a'.repeat(10)),
+ });
+ expect(result.statusCode).toBe(200);
+ expect(result.body.name).toBe(name + '.unknown');
+ expect(result.body.comment).toBe(comment);
+ expect(result.body.isSensitive).toBe(true);
+ expect(result.body.folderId).toBe(folder.id);
+ });
+
+ test('413 too large', async () => {
+ await roleService.assign(root.id, role_tinyAttachment.id);
+
+ const name = randomString();
+ const comment = randomString();
+ const result = await postFile({
+ name: name,
+ comment: comment,
+ isSensitive: true,
+ force: true,
+ fileContent: Buffer.from('a'.repeat(11)),
+ });
+ expect(result.statusCode).toBe(413);
+ expect(result.body.error.code).toBe('MAX_FILE_SIZE_EXCEEDED');
+ });
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d4836a8a91..cce229ef5a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -652,6 +652,9 @@ importers:
'@types/sinonjs__fake-timers':
specifier: 8.1.5
version: 8.1.5
+ '@types/supertest':
+ specifier: 6.0.3
+ version: 6.0.3
'@types/tinycolor2':
specifier: 1.4.6
version: 1.4.6
@@ -706,6 +709,9 @@ importers:
simple-oauth2:
specifier: 5.1.0
version: 5.1.0
+ supertest:
+ specifier: 7.1.0
+ version: 7.1.0
packages/frontend:
dependencies:
@@ -3028,6 +3034,9 @@ packages:
peerDependencies:
'@opentelemetry/api': ^1.1.0
+ '@paralleldrive/cuid2@2.2.2':
+ resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==}
+
'@parcel/watcher-android-arm64@2.5.1':
resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
engines: {node: '>= 10.0.0'}
@@ -4141,6 +4150,9 @@ packages:
'@types/cookie@0.6.0':
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
+ '@types/cookiejar@2.1.5':
+ resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==}
+
'@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
@@ -4225,6 +4237,9 @@ packages:
'@types/mdx@2.0.3':
resolution: {integrity: sha512-IgHxcT3RC8LzFLhKwP3gbMPeaK7BM9eBH46OdapPA7yvuIUJ8H6zHZV53J8hGZcTSnt95jANt+rTBNUUc22ACQ==}
+ '@types/methods@1.1.4':
+ resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==}
+
'@types/micromatch@4.0.9':
resolution: {integrity: sha512-7V+8ncr22h4UoYRLnLXSpTxjQrNUXtWHGeMPRJt1nULXI57G9bIcpyrHlmrQ7QK24EyyuXvYcSSWAM8GA9nqCg==}
@@ -4357,6 +4372,12 @@ packages:
'@types/statuses@2.0.4':
resolution: {integrity: sha512-eqNDvZsCNY49OAXB0Firg/Sc2BgoWsntsLUdybGFOhAfCD6QJ2n9HXUIHGqt5qjrxmMv4wS8WLAw43ZkKcJ8Pw==}
+ '@types/superagent@8.1.9':
+ resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==}
+
+ '@types/supertest@6.0.3':
+ resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==}
+
'@types/tedious@4.0.14':
resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==}
@@ -5404,6 +5425,9 @@ packages:
compare-versions@6.1.1:
resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==}
+ component-emitter@1.3.1:
+ resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}
+
compress-commons@6.0.2:
resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==}
engines: {node: '>= 14'}
@@ -5462,6 +5486,9 @@ packages:
resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
engines: {node: '>=18'}
+ cookiejar@2.1.4:
+ resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==}
+
core-util-is@1.0.2:
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
@@ -5773,6 +5800,9 @@ packages:
devlop@1.1.0:
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+ dezalgo@1.0.4:
+ resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==}
+
diff-match-patch@1.0.5:
resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==}
@@ -6416,6 +6446,10 @@ packages:
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
engines: {node: '>=12.20.0'}
+ formidable@3.5.4:
+ resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==}
+ engines: {node: '>=14.0.0'}
+
forwarded-parse@2.1.2:
resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==}
@@ -9809,6 +9843,14 @@ packages:
peerDependencies:
postcss: ^8.4.31
+ superagent@9.0.2:
+ resolution: {integrity: sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==}
+ engines: {node: '>=14.18.0'}
+
+ supertest@7.1.0:
+ resolution: {integrity: sha512-5QeSO8hSrKghtcWEoPiO036fxH0Ii2wVQfFZSP0oqQhmjk8bOLhDFXr4JrvaFmPuEWUoq4znY3uSi8UzLKxGqw==}
+ engines: {node: '>=14.18.0'}
+
supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
@@ -12938,6 +12980,10 @@ snapshots:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0)
+ '@paralleldrive/cuid2@2.2.2':
+ dependencies:
+ '@noble/hashes': 1.7.1
+
'@parcel/watcher-android-arm64@2.5.1':
optional: true
@@ -14296,6 +14342,8 @@ snapshots:
'@types/cookie@0.6.0': {}
+ '@types/cookiejar@2.1.5': {}
+
'@types/debug@4.1.12':
dependencies:
'@types/ms': 0.7.34
@@ -14386,6 +14434,8 @@ snapshots:
'@types/mdx@2.0.3': {}
+ '@types/methods@1.1.4': {}
+
'@types/micromatch@4.0.9':
dependencies:
'@types/braces': 3.0.1
@@ -14518,6 +14568,18 @@ snapshots:
'@types/statuses@2.0.4': {}
+ '@types/superagent@8.1.9':
+ dependencies:
+ '@types/cookiejar': 2.1.5
+ '@types/methods': 1.1.4
+ '@types/node': 22.15.2
+ form-data: 4.0.2
+
+ '@types/supertest@6.0.3':
+ dependencies:
+ '@types/methods': 1.1.4
+ '@types/superagent': 8.1.9
+
'@types/tedious@4.0.14':
dependencies:
'@types/node': 22.15.2
@@ -15843,6 +15905,8 @@ snapshots:
compare-versions@6.1.1: {}
+ component-emitter@1.3.1: {}
+
compress-commons@6.0.2:
dependencies:
crc-32: 1.2.2
@@ -15896,6 +15960,8 @@ snapshots:
cookie@1.0.2: {}
+ cookiejar@2.1.4: {}
+
core-util-is@1.0.2: {}
core-util-is@1.0.3: {}
@@ -16281,6 +16347,11 @@ snapshots:
dependencies:
dequal: 2.0.3
+ dezalgo@1.0.4:
+ dependencies:
+ asap: 2.0.6
+ wrappy: 1.0.2
+
diff-match-patch@1.0.5: {}
diff-sequences@29.6.3: {}
@@ -17175,6 +17246,12 @@ snapshots:
dependencies:
fetch-blob: 3.2.0
+ formidable@3.5.4:
+ dependencies:
+ '@paralleldrive/cuid2': 2.2.2
+ dezalgo: 1.0.4
+ once: 1.4.0
+
forwarded-parse@2.1.2: {}
forwarded@0.2.0: {}
@@ -21128,6 +21205,27 @@ snapshots:
postcss: 8.5.3
postcss-selector-parser: 6.1.2
+ superagent@9.0.2:
+ dependencies:
+ component-emitter: 1.3.1
+ cookiejar: 2.1.4
+ debug: 4.4.0(supports-color@8.1.1)
+ fast-safe-stringify: 2.1.1
+ form-data: 4.0.2
+ formidable: 3.5.4
+ methods: 1.1.2
+ mime: 2.6.0
+ qs: 6.14.0
+ transitivePeerDependencies:
+ - supports-color
+
+ supertest@7.1.0:
+ dependencies:
+ methods: 1.1.2
+ superagent: 9.0.2
+ transitivePeerDependencies:
+ - supports-color
+
supports-color@5.5.0:
dependencies:
has-flag: 3.0.0