fix(server): storage usage should be float rather than int (#4275)
This commit is contained in:
@@ -101,7 +101,7 @@ export class InvitationWorkspaceType {
|
|||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
export class WorkspaceBlobSizes {
|
export class WorkspaceBlobSizes {
|
||||||
@Field(() => Int)
|
@Field(() => Float)
|
||||||
size!: number;
|
size!: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ type InvitationWorkspaceType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WorkspaceBlobSizes {
|
type WorkspaceBlobSizes {
|
||||||
size: Int!
|
size: Float!
|
||||||
}
|
}
|
||||||
|
|
||||||
type InvitationType {
|
type InvitationType {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { INestApplication } from '@nestjs/common';
|
|||||||
import { Test } from '@nestjs/testing';
|
import { Test } from '@nestjs/testing';
|
||||||
import { hashSync } from '@node-rs/argon2';
|
import { hashSync } from '@node-rs/argon2';
|
||||||
import { User } from '@prisma/client';
|
import { User } from '@prisma/client';
|
||||||
import test from 'ava';
|
import ava, { TestFn } from 'ava';
|
||||||
import { Express } from 'express';
|
import { Express } from 'express';
|
||||||
// @ts-expect-error graphql-upload is not typed
|
// @ts-expect-error graphql-upload is not typed
|
||||||
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
||||||
@@ -17,7 +17,9 @@ import { PrismaService } from '../prisma/service';
|
|||||||
|
|
||||||
const gql = '/graphql';
|
const gql = '/graphql';
|
||||||
|
|
||||||
let app: INestApplication;
|
const test = ava as TestFn<{
|
||||||
|
app: INestApplication;
|
||||||
|
}>;
|
||||||
|
|
||||||
class FakePrisma {
|
class FakePrisma {
|
||||||
fakeUser: User = {
|
fakeUser: User = {
|
||||||
@@ -46,33 +48,33 @@ class FakePrisma {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test.beforeEach(async () => {
|
test.beforeEach(async t => {
|
||||||
const module = await Test.createTestingModule({
|
const module = await Test.createTestingModule({
|
||||||
imports: [AppModule],
|
imports: [AppModule],
|
||||||
})
|
})
|
||||||
.overrideProvider(PrismaService)
|
.overrideProvider(PrismaService)
|
||||||
.useClass(FakePrisma)
|
.useClass(FakePrisma)
|
||||||
.compile();
|
.compile();
|
||||||
app = module.createNestApplication({
|
t.context.app = module.createNestApplication({
|
||||||
cors: true,
|
cors: true,
|
||||||
bodyParser: true,
|
bodyParser: true,
|
||||||
});
|
});
|
||||||
app.use(
|
t.context.app.use(
|
||||||
graphqlUploadExpress({
|
graphqlUploadExpress({
|
||||||
maxFileSize: 10 * 1024 * 1024,
|
maxFileSize: 10 * 1024 * 1024,
|
||||||
maxFiles: 5,
|
maxFiles: 5,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
await app.init();
|
await t.context.app.init();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.afterEach(async () => {
|
test.afterEach(async t => {
|
||||||
await app.close();
|
await t.context.app.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should init app', async t => {
|
test('should init app', async t => {
|
||||||
t.is(typeof app, 'object');
|
t.is(typeof t.context.app, 'object');
|
||||||
await request(app.getHttpServer())
|
await request(t.context.app.getHttpServer())
|
||||||
.post(gql)
|
.post(gql)
|
||||||
.send({
|
.send({
|
||||||
query: `
|
query: `
|
||||||
@@ -83,9 +85,9 @@ test('should init app', async t => {
|
|||||||
})
|
})
|
||||||
.expect(400);
|
.expect(400);
|
||||||
|
|
||||||
const { token } = await createToken(app);
|
const { token } = await createToken(t.context.app);
|
||||||
|
|
||||||
await request(app.getHttpServer())
|
await request(t.context.app.getHttpServer())
|
||||||
.post(gql)
|
.post(gql)
|
||||||
.auth(token, { type: 'bearer' })
|
.auth(token, { type: 'bearer' })
|
||||||
.send({
|
.send({
|
||||||
@@ -102,8 +104,8 @@ test('should init app', async t => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should find default user', async t => {
|
test('should find default user', async t => {
|
||||||
const { token } = await createToken(app);
|
const { token } = await createToken(t.context.app);
|
||||||
await request(app.getHttpServer())
|
await request(t.context.app.getHttpServer())
|
||||||
.post(gql)
|
.post(gql)
|
||||||
.auth(token, { type: 'bearer' })
|
.auth(token, { type: 'bearer' })
|
||||||
.send({
|
.send({
|
||||||
@@ -123,14 +125,14 @@ test('should find default user', async t => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should be able to upload avatar', async t => {
|
test('should be able to upload avatar', async t => {
|
||||||
const { token, id } = await createToken(app);
|
const { token, id } = await createToken(t.context.app);
|
||||||
const png = await Transformer.fromRgbaPixels(
|
const png = await Transformer.fromRgbaPixels(
|
||||||
Buffer.alloc(400 * 400 * 4).fill(255),
|
Buffer.alloc(400 * 400 * 4).fill(255),
|
||||||
400,
|
400,
|
||||||
400
|
400
|
||||||
).png();
|
).png();
|
||||||
|
|
||||||
await request(app.getHttpServer())
|
await request(t.context.app.getHttpServer())
|
||||||
.post(gql)
|
.post(gql)
|
||||||
.auth(token, { type: 'bearer' })
|
.auth(token, { type: 'bearer' })
|
||||||
.field(
|
.field(
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
import { randomUUID } from 'node:crypto';
|
||||||
|
|
||||||
import type { INestApplication } from '@nestjs/common';
|
import type { INestApplication } from '@nestjs/common';
|
||||||
import { Test } from '@nestjs/testing';
|
import { Test } from '@nestjs/testing';
|
||||||
import { PrismaClient } from '@prisma/client';
|
import { hashSync } from '@node-rs/argon2';
|
||||||
|
import { PrismaClient, User } from '@prisma/client';
|
||||||
// @ts-expect-error graphql-upload is not typed
|
// @ts-expect-error graphql-upload is not typed
|
||||||
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
@@ -498,6 +501,33 @@ async function getInviteInfo(
|
|||||||
return res.body.data.getInviteInfo;
|
return res.body.data.getInviteInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class FakePrisma {
|
||||||
|
fakeUser: User = {
|
||||||
|
id: randomUUID(),
|
||||||
|
name: 'Alex Yang',
|
||||||
|
avatarUrl: '',
|
||||||
|
email: 'alex.yang@example.org',
|
||||||
|
password: hashSync('123456'),
|
||||||
|
emailVerified: new Date(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
};
|
||||||
|
get user() {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
|
const prisma = this;
|
||||||
|
return {
|
||||||
|
async findFirst() {
|
||||||
|
return prisma.fakeUser;
|
||||||
|
},
|
||||||
|
async findUnique() {
|
||||||
|
return this.findFirst();
|
||||||
|
},
|
||||||
|
async update() {
|
||||||
|
return this.findFirst();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
acceptInvite,
|
acceptInvite,
|
||||||
acceptInviteById,
|
acceptInviteById,
|
||||||
|
|||||||
76
apps/server/src/tests/workspace-usage.spec.ts
Normal file
76
apps/server/src/tests/workspace-usage.spec.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import type { INestApplication } from '@nestjs/common';
|
||||||
|
import { Test } from '@nestjs/testing';
|
||||||
|
import ava, { TestFn } from 'ava';
|
||||||
|
import { stub } from 'sinon';
|
||||||
|
|
||||||
|
import { AppModule } from '../app';
|
||||||
|
import { UsersService } from '../modules/users';
|
||||||
|
import { PermissionService } from '../modules/workspaces/permission';
|
||||||
|
import { WorkspaceResolver } from '../modules/workspaces/resolver';
|
||||||
|
import { PrismaService } from '../prisma';
|
||||||
|
import { StorageProvide } from '../storage';
|
||||||
|
import { FakePrisma } from './utils';
|
||||||
|
|
||||||
|
class FakePermission {
|
||||||
|
async tryCheck() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
async getWorkspaceOwner() {
|
||||||
|
return {
|
||||||
|
user: new FakePrisma().fakeUser,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fakeUserService = {
|
||||||
|
getStorageQuotaById: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const test = ava as TestFn<{
|
||||||
|
app: INestApplication;
|
||||||
|
resolver: WorkspaceResolver;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
test.beforeEach(async t => {
|
||||||
|
const module = await Test.createTestingModule({
|
||||||
|
imports: [AppModule],
|
||||||
|
})
|
||||||
|
.overrideProvider(PrismaService)
|
||||||
|
.useValue({
|
||||||
|
userWorkspacePermission: {
|
||||||
|
async findMany() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.overrideProvider(PermissionService)
|
||||||
|
.useClass(FakePermission)
|
||||||
|
.overrideProvider(UsersService)
|
||||||
|
.useValue(fakeUserService)
|
||||||
|
.overrideProvider(StorageProvide)
|
||||||
|
.useValue({
|
||||||
|
blobsSize() {
|
||||||
|
return 1024 * 10;
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.compile();
|
||||||
|
t.context.app = module.createNestApplication();
|
||||||
|
t.context.resolver = t.context.app.get(WorkspaceResolver);
|
||||||
|
await t.context.app.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.afterEach(async t => {
|
||||||
|
await t.context.app.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should get blob size limit', async t => {
|
||||||
|
const { resolver } = t.context;
|
||||||
|
fakeUserService.getStorageQuotaById.returns(
|
||||||
|
Promise.resolve(100 * 1024 * 1024 * 1024)
|
||||||
|
);
|
||||||
|
const res = await resolver.checkBlobSize(new FakePrisma().fakeUser, '', 100);
|
||||||
|
t.not(res, false);
|
||||||
|
// @ts-expect-error
|
||||||
|
t.is(typeof res.size, 'number');
|
||||||
|
fakeUserService.getStorageQuotaById.reset();
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user