chore(server): move server tests folder (#9614)

This commit is contained in:
forehalo
2025-01-10 02:38:10 +00:00
parent 8e8058a44c
commit 1b6f0e78c4
54 changed files with 166 additions and 186 deletions

View File

@@ -0,0 +1,165 @@
import type { INestApplication } from '@nestjs/common';
import test from 'ava';
import request from 'supertest';
import { AppModule } from '../../app.module';
import { FeatureManagementService, FeatureType } from '../../core/features';
import { QuotaService, QuotaType } from '../../core/quota';
import {
collectAllBlobSizes,
createTestingApp,
createWorkspace,
getWorkspaceBlobsSize,
listBlobs,
setBlob,
signUp,
} from '../utils';
const OneMB = 1024 * 1024;
let app: INestApplication;
let quota: QuotaService;
let feature: FeatureManagementService;
test.beforeEach(async () => {
const { app: testApp } = await createTestingApp({
imports: [AppModule],
});
app = testApp;
quota = app.get(QuotaService);
feature = app.get(FeatureManagementService);
});
test.afterEach.always(async () => {
await app.close();
});
test('should set blobs', async t => {
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
const workspace = await createWorkspace(app, u1.token.token);
const buffer1 = Buffer.from([0, 0]);
const hash1 = await setBlob(app, u1.token.token, workspace.id, buffer1);
const buffer2 = Buffer.from([0, 1]);
const hash2 = await setBlob(app, u1.token.token, workspace.id, buffer2);
const server = app.getHttpServer();
const response1 = await request(server)
.get(`/api/workspaces/${workspace.id}/blobs/${hash1}`)
.auth(u1.token.token, { type: 'bearer' })
.buffer();
t.deepEqual(response1.body, buffer1, 'failed to get blob');
const response2 = await request(server)
.get(`/api/workspaces/${workspace.id}/blobs/${hash2}`)
.auth(u1.token.token, { type: 'bearer' })
.buffer();
t.deepEqual(response2.body, buffer2, 'failed to get blob');
});
test('should list blobs', async t => {
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
const workspace = await createWorkspace(app, u1.token.token);
const blobs = await listBlobs(app, u1.token.token, workspace.id);
t.is(blobs.length, 0, 'failed to list blobs');
const buffer1 = Buffer.from([0, 0]);
const hash1 = await setBlob(app, u1.token.token, workspace.id, buffer1);
const buffer2 = Buffer.from([0, 1]);
const hash2 = await setBlob(app, u1.token.token, workspace.id, buffer2);
const ret = await listBlobs(app, u1.token.token, workspace.id);
t.is(ret.length, 2, 'failed to list blobs');
// list blob result is not ordered
t.deepEqual(ret.sort(), [hash1, hash2].sort());
});
test('should calc blobs size', async t => {
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
const workspace = await createWorkspace(app, u1.token.token);
const buffer1 = Buffer.from([0, 0]);
await setBlob(app, u1.token.token, workspace.id, buffer1);
const buffer2 = Buffer.from([0, 1]);
await setBlob(app, u1.token.token, workspace.id, buffer2);
const size = await getWorkspaceBlobsSize(app, u1.token.token, workspace.id);
t.is(size, 4, 'failed to collect blob sizes');
});
test('should calc all blobs size', async t => {
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
const workspace1 = await createWorkspace(app, u1.token.token);
const buffer1 = Buffer.from([0, 0]);
await setBlob(app, u1.token.token, workspace1.id, buffer1);
const buffer2 = Buffer.from([0, 1]);
await setBlob(app, u1.token.token, workspace1.id, buffer2);
const workspace2 = await createWorkspace(app, u1.token.token);
const buffer3 = Buffer.from([0, 0]);
await setBlob(app, u1.token.token, workspace2.id, buffer3);
const buffer4 = Buffer.from([0, 1]);
await setBlob(app, u1.token.token, workspace2.id, buffer4);
const size = await collectAllBlobSizes(app, u1.token.token);
t.is(size, 8, 'failed to collect all blob sizes');
});
test('should reject blob exceeded limit', async t => {
const u1 = await signUp(app, 'darksky', 'darksky@affine.pro', '1');
const workspace1 = await createWorkspace(app, u1.token.token);
await quota.switchUserQuota(u1.id, QuotaType.RestrictedPlanV1);
const buffer1 = Buffer.from(Array.from({ length: OneMB + 1 }, () => 0));
await t.throwsAsync(setBlob(app, u1.token.token, workspace1.id, buffer1));
await quota.switchUserQuota(u1.id, QuotaType.FreePlanV1);
const buffer2 = Buffer.from(Array.from({ length: OneMB + 1 }, () => 0));
await t.notThrowsAsync(setBlob(app, u1.token.token, workspace1.id, buffer2));
const buffer3 = Buffer.from(Array.from({ length: 100 * OneMB + 1 }, () => 0));
await t.throwsAsync(setBlob(app, u1.token.token, workspace1.id, buffer3));
});
test('should reject blob exceeded quota', async t => {
const u1 = await signUp(app, 'darksky', 'darksky@affine.pro', '1');
const workspace = await createWorkspace(app, u1.token.token);
await quota.switchUserQuota(u1.id, QuotaType.RestrictedPlanV1);
const buffer = Buffer.from(Array.from({ length: OneMB }, () => 0));
for (let i = 0; i < 10; i++) {
await t.notThrowsAsync(setBlob(app, u1.token.token, workspace.id, buffer));
}
await t.throwsAsync(setBlob(app, u1.token.token, workspace.id, buffer));
});
test('should accept blob even storage out of quota if workspace has unlimited feature', async t => {
const u1 = await signUp(app, 'darksky', 'darksky@affine.pro', '1');
const workspace = await createWorkspace(app, u1.token.token);
await quota.switchUserQuota(u1.id, QuotaType.RestrictedPlanV1);
feature.addWorkspaceFeatures(workspace.id, FeatureType.UnlimitedWorkspace);
const buffer = Buffer.from(Array.from({ length: OneMB }, () => 0));
for (let i = 0; i < 10; i++) {
await t.notThrowsAsync(setBlob(app, u1.token.token, workspace.id, buffer));
}
await t.notThrowsAsync(setBlob(app, u1.token.token, workspace.id, buffer));
});

View File

@@ -0,0 +1,275 @@
import { Readable } from 'node:stream';
import { HttpStatus, INestApplication } from '@nestjs/common';
import { PrismaClient, WorkspaceMemberStatus } from '@prisma/client';
import ava, { TestFn } from 'ava';
import Sinon from 'sinon';
import request from 'supertest';
import { AppModule } from '../../app.module';
import { CurrentUser } from '../../core/auth';
import { AuthService } from '../../core/auth/service';
import { PgWorkspaceDocStorageAdapter } from '../../core/doc';
import { WorkspaceBlobStorage } from '../../core/storage';
import { createTestingApp, internalSignIn } from '../utils';
const test = ava as TestFn<{
u1: CurrentUser;
db: PrismaClient;
app: INestApplication;
storage: Sinon.SinonStubbedInstance<WorkspaceBlobStorage>;
workspace: Sinon.SinonStubbedInstance<PgWorkspaceDocStorageAdapter>;
}>;
test.before(async t => {
const { app } = await createTestingApp({
imports: [AppModule],
tapModule: m => {
m.overrideProvider(WorkspaceBlobStorage)
.useValue(Sinon.createStubInstance(WorkspaceBlobStorage))
.overrideProvider(PgWorkspaceDocStorageAdapter)
.useValue(Sinon.createStubInstance(PgWorkspaceDocStorageAdapter));
},
});
const auth = app.get(AuthService);
t.context.u1 = await auth.signUp('u1@affine.pro', '1');
const db = app.get(PrismaClient);
t.context.db = db;
t.context.app = app;
t.context.storage = app.get(WorkspaceBlobStorage);
t.context.workspace = app.get(PgWorkspaceDocStorageAdapter);
await db.workspacePage.create({
data: {
workspace: {
create: {
id: 'public',
public: true,
},
},
pageId: 'private',
public: false,
},
});
await db.workspacePage.create({
data: {
workspace: {
create: {
id: 'private',
public: false,
},
},
pageId: 'public',
public: true,
},
});
await db.workspacePage.create({
data: {
workspace: {
create: {
id: 'totally-private',
public: false,
},
},
pageId: 'private',
public: false,
},
});
});
test.after.always(async t => {
await t.context.app.close();
});
function blob() {
function stream() {
return Readable.from(Buffer.from('blob'));
}
const init = stream();
const ret = {
body: init,
metadata: {
contentType: 'text/plain',
lastModified: new Date(),
contentLength: 4,
},
};
init.on('end', () => {
ret.body = stream();
});
return ret;
}
// blob
test('should be able to get blob from public workspace', async t => {
const { app, u1, storage } = t.context;
// no authenticated user
storage.get.resolves(blob());
let res = await request(t.context.app.getHttpServer()).get(
'/api/workspaces/public/blobs/test'
);
t.is(res.status, HttpStatus.OK);
t.is(res.get('content-type'), 'text/plain');
t.is(res.text, 'blob');
// authenticated user
const cookie = await internalSignIn(app, u1.id);
res = await request(t.context.app.getHttpServer())
.get('/api/workspaces/public/blobs/test')
.set('Cookie', cookie);
t.is(res.status, HttpStatus.OK);
t.is(res.get('content-type'), 'text/plain');
t.is(res.text, 'blob');
});
test('should be able to get private workspace with public pages', async t => {
const { app, u1, storage } = t.context;
// no authenticated user
storage.get.resolves(blob());
let res = await request(app.getHttpServer()).get(
'/api/workspaces/private/blobs/test'
);
t.is(res.status, HttpStatus.OK);
t.is(res.get('content-type'), 'text/plain');
t.is(res.text, 'blob');
// authenticated user
const cookie = await internalSignIn(app, u1.id);
res = await request(app.getHttpServer())
.get('/api/workspaces/private/blobs/test')
.set('cookie', cookie);
t.is(res.status, HttpStatus.OK);
t.is(res.get('content-type'), 'text/plain');
t.is(res.text, 'blob');
});
test('should not be able to get private workspace with no public pages', async t => {
const { app, u1 } = t.context;
let res = await request(app.getHttpServer()).get(
'/api/workspaces/totally-private/blobs/test'
);
t.is(res.status, HttpStatus.FORBIDDEN);
res = await request(app.getHttpServer())
.get('/api/workspaces/totally-private/blobs/test')
.set('cookie', await internalSignIn(app, u1.id));
t.is(res.status, HttpStatus.FORBIDDEN);
});
test('should be able to get permission granted workspace', async t => {
const { app, u1, db, storage } = t.context;
const cookie = await internalSignIn(app, u1.id);
await db.workspaceUserPermission.create({
data: {
workspaceId: 'totally-private',
userId: u1.id,
type: 1,
accepted: true,
status: WorkspaceMemberStatus.Accepted,
},
});
storage.get.resolves(blob());
const res = await request(app.getHttpServer())
.get('/api/workspaces/totally-private/blobs/test')
.set('Cookie', cookie);
t.is(res.status, HttpStatus.OK);
t.is(res.text, 'blob');
});
test('should return 404 if blob not found', async t => {
const { app, storage } = t.context;
// @ts-expect-error mock
storage.get.resolves({ body: null });
const res = await request(app.getHttpServer()).get(
'/api/workspaces/public/blobs/test'
);
t.is(res.status, HttpStatus.NOT_FOUND);
});
// doc
// NOTE: permission checking of doc api is the same with blob api, skip except one
test('should not be able to get private workspace with private page', async t => {
const { app, u1 } = t.context;
let res = await request(app.getHttpServer()).get(
'/api/workspaces/private/docs/private-page'
);
t.is(res.status, HttpStatus.FORBIDDEN);
res = await request(app.getHttpServer())
.get('/api/workspaces/private/docs/private-page')
.set('cookie', await internalSignIn(app, u1.id));
t.is(res.status, HttpStatus.FORBIDDEN);
});
test('should be able to get doc', async t => {
const { app, workspace: doc } = t.context;
doc.getDoc.resolves({
spaceId: '',
docId: '',
bin: Buffer.from([0, 0]),
timestamp: Date.now(),
});
const res = await request(app.getHttpServer()).get(
'/api/workspaces/private/docs/public'
);
t.is(res.status, HttpStatus.OK);
t.is(res.get('content-type'), 'application/octet-stream');
t.deepEqual(res.body, Buffer.from([0, 0]));
});
test('should be able to change page publish mode', async t => {
const { app, workspace: doc, db } = t.context;
doc.getDoc.resolves({
spaceId: '',
docId: '',
bin: Buffer.from([0, 0]),
timestamp: Date.now(),
});
let res = await request(app.getHttpServer()).get(
'/api/workspaces/private/docs/public'
);
t.is(res.status, HttpStatus.OK);
t.is(res.get('publish-mode'), 'page');
await db.workspacePage.update({
where: { workspaceId_pageId: { workspaceId: 'private', pageId: 'public' } },
data: { mode: 1 },
});
res = await request(app.getHttpServer()).get(
'/api/workspaces/private/docs/public'
);
t.is(res.status, HttpStatus.OK);
t.is(res.get('publish-mode'), 'edgeless');
});