mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-01 22:29:44 +08:00
refactor(server): use feature model (#9932)
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
# Snapshot report for `src/__tests__/models/feature-user.spec.ts`
|
||||
|
||||
The actual snapshot is saved in `feature-user.spec.ts.snap`.
|
||||
|
||||
Generated by [AVA](https://avajs.dev).
|
||||
|
||||
## should get user quota
|
||||
|
||||
> free plan
|
||||
|
||||
{
|
||||
blobLimit: 10485760,
|
||||
businessBlobLimit: 104857600,
|
||||
copilotActionLimit: 10,
|
||||
historyPeriod: 604800000,
|
||||
memberLimit: 3,
|
||||
name: 'Free',
|
||||
storageQuota: 10737418240,
|
||||
}
|
||||
|
||||
## should switch user quota
|
||||
|
||||
> switch to pro plan
|
||||
|
||||
{
|
||||
blobLimit: 104857600,
|
||||
copilotActionLimit: 10,
|
||||
historyPeriod: 2592000000,
|
||||
memberLimit: 10,
|
||||
name: 'Pro',
|
||||
storageQuota: 107374182400,
|
||||
}
|
||||
|
||||
> switch to free plan
|
||||
|
||||
{
|
||||
blobLimit: 10485760,
|
||||
businessBlobLimit: 104857600,
|
||||
copilotActionLimit: 10,
|
||||
historyPeriod: 604800000,
|
||||
memberLimit: 3,
|
||||
name: 'Free',
|
||||
storageQuota: 10737418240,
|
||||
}
|
||||
Binary file not shown.
+18
@@ -0,0 +1,18 @@
|
||||
# Snapshot report for `src/__tests__/models/feature-workspace.spec.ts`
|
||||
|
||||
The actual snapshot is saved in `feature-workspace.spec.ts.snap`.
|
||||
|
||||
Generated by [AVA](https://avajs.dev).
|
||||
|
||||
## should get workspace quota
|
||||
|
||||
> team plan
|
||||
|
||||
{
|
||||
blobLimit: 524288000,
|
||||
historyPeriod: 2592000000,
|
||||
memberLimit: 100,
|
||||
name: 'Team Workspace',
|
||||
seatQuota: 21474836480,
|
||||
storageQuota: 2254857830400,
|
||||
}
|
||||
BIN
Binary file not shown.
@@ -10,6 +10,7 @@ Generated by [AVA](https://avajs.dev).
|
||||
|
||||
{
|
||||
blobLimit: 10485760,
|
||||
businessBlobLimit: 104857600,
|
||||
copilotActionLimit: 10,
|
||||
historyPeriod: 604800000,
|
||||
memberLimit: 3,
|
||||
@@ -23,6 +24,7 @@ Generated by [AVA](https://avajs.dev).
|
||||
|
||||
{
|
||||
blobLimit: 10485760,
|
||||
businessBlobLimit: 104857600,
|
||||
copilotActionLimit: 10,
|
||||
historyPeriod: 604800000,
|
||||
memberLimit: 3,
|
||||
|
||||
Binary file not shown.
@@ -1,9 +1,8 @@
|
||||
import { TestingModule } from '@nestjs/testing';
|
||||
import { PrismaClient, User } from '@prisma/client';
|
||||
import { User } from '@prisma/client';
|
||||
import ava, { TestFn } from 'ava';
|
||||
|
||||
import { UserFeatureModel, UserModel } from '../../models';
|
||||
import { createTestingModule, initTestingDB } from '../utils';
|
||||
import { FeatureType, UserFeatureModel, UserModel } from '../../models';
|
||||
import { createTestingModule, TestingModule } from '../utils';
|
||||
|
||||
interface Context {
|
||||
module: TestingModule;
|
||||
@@ -21,7 +20,7 @@ test.before(async t => {
|
||||
});
|
||||
|
||||
test.beforeEach(async t => {
|
||||
await initTestingDB(t.context.module.get(PrismaClient));
|
||||
await t.context.module.initTestingDB();
|
||||
t.context.u1 = await t.context.module.get(UserModel).create({
|
||||
email: 'u1@affine.pro',
|
||||
registered: true,
|
||||
@@ -41,7 +40,13 @@ test('should get null if user feature not found', async t => {
|
||||
test('should get user feature', async t => {
|
||||
const { model, u1 } = t.context;
|
||||
const userFeature = await model.get(u1.id, 'free_plan_v1');
|
||||
t.is(userFeature?.feature, 'free_plan_v1');
|
||||
t.is(userFeature?.name, 'free_plan_v1');
|
||||
});
|
||||
|
||||
test('should get user quota', async t => {
|
||||
const { model, u1 } = t.context;
|
||||
const userQuota = await model.getQuota(u1.id);
|
||||
t.snapshot(userQuota?.configs, 'free plan');
|
||||
});
|
||||
|
||||
test('should list user features', async t => {
|
||||
@@ -50,6 +55,16 @@ test('should list user features', async t => {
|
||||
t.like(await model.list(u1.id), ['free_plan_v1']);
|
||||
});
|
||||
|
||||
test('should list user features by type', async t => {
|
||||
const { model, u1 } = t.context;
|
||||
|
||||
await model.add(u1.id, 'free_plan_v1', 'test');
|
||||
await model.add(u1.id, 'unlimited_copilot', 'test');
|
||||
|
||||
t.like(await model.list(u1.id, FeatureType.Quota), ['free_plan_v1']);
|
||||
t.like(await model.list(u1.id, FeatureType.Feature), ['unlimited_copilot']);
|
||||
});
|
||||
|
||||
test('should directly test user feature existence', async t => {
|
||||
const { model, u1 } = t.context;
|
||||
|
||||
@@ -82,14 +97,29 @@ test('should remove user feature', async t => {
|
||||
t.false((await model.list(u1.id)).includes('free_plan_v1'));
|
||||
});
|
||||
|
||||
test('should switch user feature', async t => {
|
||||
test('should switch user quota', async t => {
|
||||
const { model, u1 } = t.context;
|
||||
|
||||
await model.switch(u1.id, 'free_plan_v1', 'pro_plan_v1', 'test');
|
||||
await model.switchQuota(u1.id, 'pro_plan_v1', 'test');
|
||||
const quota = await model.getQuota(u1.id);
|
||||
t.snapshot(quota?.configs, 'switch to pro plan');
|
||||
|
||||
t.false(await model.has(u1.id, 'free_plan_v1'));
|
||||
t.true(await model.has(u1.id, 'pro_plan_v1'));
|
||||
|
||||
t.false((await model.list(u1.id)).includes('free_plan_v1'));
|
||||
t.true((await model.list(u1.id)).includes('pro_plan_v1'));
|
||||
await model.switchQuota(u1.id, 'free_plan_v1', 'test');
|
||||
const quota2 = await model.getQuota(u1.id);
|
||||
t.snapshot(quota2?.configs, 'switch to free plan');
|
||||
});
|
||||
|
||||
test('should not switch user quota if the new quota is the same as the current one', async t => {
|
||||
const { model, u1 } = t.context;
|
||||
|
||||
await model.switchQuota(u1.id, 'free_plan_v1', 'test not switch');
|
||||
|
||||
// @ts-expect-error private
|
||||
const quota = await model.db.userFeature.findFirst({
|
||||
where: {
|
||||
userId: u1.id,
|
||||
},
|
||||
});
|
||||
|
||||
t.not(quota?.reason, 'test not switch');
|
||||
});
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { TestingModule } from '@nestjs/testing';
|
||||
import { PrismaClient, Workspace } from '@prisma/client';
|
||||
import { Workspace } from '@prisma/client';
|
||||
import ava, { TestFn } from 'ava';
|
||||
|
||||
import { UserModel, WorkspaceFeatureModel, WorkspaceModel } from '../../models';
|
||||
import { createTestingModule, initTestingDB } from '../utils';
|
||||
import {
|
||||
FeatureType,
|
||||
UserModel,
|
||||
WorkspaceFeatureModel,
|
||||
WorkspaceModel,
|
||||
} from '../../models';
|
||||
import { createTestingModule, type TestingModule } from '../utils';
|
||||
|
||||
interface Context {
|
||||
module: TestingModule;
|
||||
@@ -21,7 +25,7 @@ test.before(async t => {
|
||||
});
|
||||
|
||||
test.beforeEach(async t => {
|
||||
await initTestingDB(t.context.module.get(PrismaClient));
|
||||
await t.context.module.initTestingDB();
|
||||
const u1 = await t.context.module.get(UserModel).create({
|
||||
email: 'u1@affine.pro',
|
||||
registered: true,
|
||||
@@ -46,18 +50,52 @@ test('should directly test workspace feature existence', async t => {
|
||||
t.false(await model.has(ws.id, 'unlimited_workspace'));
|
||||
});
|
||||
|
||||
test('should get workspace quota', async t => {
|
||||
const { model, ws } = t.context;
|
||||
|
||||
await model.add(ws.id, 'team_plan_v1', 'test', {
|
||||
memberLimit: 100,
|
||||
});
|
||||
|
||||
const quota = await model.getQuota(ws.id);
|
||||
t.snapshot(quota?.configs, 'team plan');
|
||||
});
|
||||
|
||||
test('should return null if quota removed', async t => {
|
||||
const { model, ws } = t.context;
|
||||
|
||||
await model.add(ws.id, 'team_plan_v1', 'test', {
|
||||
memberLimit: 100,
|
||||
});
|
||||
|
||||
await model.remove(ws.id, 'team_plan_v1');
|
||||
|
||||
const quota = await model.getQuota(ws.id);
|
||||
t.is(quota, null);
|
||||
});
|
||||
|
||||
test('should list empty workspace features', async t => {
|
||||
const { model, ws } = t.context;
|
||||
|
||||
t.deepEqual(await model.list(ws.id), []);
|
||||
});
|
||||
|
||||
test('should list workspace features by type', async t => {
|
||||
const { model, ws } = t.context;
|
||||
|
||||
await model.add(ws.id, 'unlimited_workspace', 'test');
|
||||
await model.add(ws.id, 'team_plan_v1', 'test');
|
||||
|
||||
t.like(await model.list(ws.id, FeatureType.Quota), ['team_plan_v1']);
|
||||
t.like(await model.list(ws.id, FeatureType.Feature), ['unlimited_workspace']);
|
||||
});
|
||||
|
||||
test('should add workspace feature', async t => {
|
||||
const { model, ws } = t.context;
|
||||
|
||||
await model.add(ws.id, 'unlimited_workspace', 'test');
|
||||
t.is(
|
||||
(await model.get(ws.id, 'unlimited_workspace'))?.feature,
|
||||
(await model.get(ws.id, 'unlimited_workspace'))?.name,
|
||||
'unlimited_workspace'
|
||||
);
|
||||
t.true(await model.has(ws.id, 'unlimited_workspace'));
|
||||
@@ -103,25 +141,3 @@ test('should remove workspace feature', async t => {
|
||||
t.false(await model.has(ws.id, 'team_plan_v1'));
|
||||
t.false((await model.list(ws.id)).includes('team_plan_v1'));
|
||||
});
|
||||
|
||||
test('should switch workspace feature', async t => {
|
||||
const { model, ws } = t.context;
|
||||
|
||||
await model.switch(ws.id, 'team_plan_v1', 'unlimited_workspace', 'test');
|
||||
|
||||
t.false(await model.has(ws.id, 'team_plan_v1'));
|
||||
t.true(await model.has(ws.id, 'unlimited_workspace'));
|
||||
|
||||
t.false((await model.list(ws.id)).includes('team_plan_v1'));
|
||||
t.true((await model.list(ws.id)).includes('unlimited_workspace'));
|
||||
});
|
||||
|
||||
test('should switch workspace feature with overrides', async t => {
|
||||
const { model, ws } = t.context;
|
||||
|
||||
await model.add(ws.id, 'unlimited_workspace', 'test');
|
||||
await model.add(ws.id, 'team_plan_v1', 'test', { memberLimit: 100 });
|
||||
const f2 = await model.get(ws.id, 'team_plan_v1');
|
||||
|
||||
t.is(f2!.configs.memberLimit, 100);
|
||||
});
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { TestingModule } from '@nestjs/testing';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import ava, { TestFn } from 'ava';
|
||||
|
||||
import { FeatureType } from '../../models';
|
||||
import { FeatureModel } from '../../models/feature';
|
||||
import { createTestingModule, initTestingDB } from '../utils';
|
||||
import { createTestingModule, type TestingModule } from '../utils';
|
||||
|
||||
interface Context {
|
||||
module: TestingModule;
|
||||
@@ -20,7 +19,7 @@ test.before(async t => {
|
||||
});
|
||||
|
||||
test.beforeEach(async t => {
|
||||
await initTestingDB(t.context.module.get(PrismaClient));
|
||||
await t.context.module.initTestingDB();
|
||||
});
|
||||
|
||||
test.after(async t => {
|
||||
@@ -91,7 +90,12 @@ test('should get feature if extra fields exist in feature config', async t => {
|
||||
test('should create feature', async t => {
|
||||
const { feature } = t.context;
|
||||
|
||||
const newFeature = await feature.upsert('new_feature' as any, {});
|
||||
const newFeature = await feature.upsert(
|
||||
'new_feature' as any,
|
||||
{},
|
||||
FeatureType.Feature,
|
||||
1
|
||||
);
|
||||
|
||||
t.deepEqual(newFeature.configs, {});
|
||||
});
|
||||
@@ -100,10 +104,15 @@ test('should update feature', async t => {
|
||||
const { feature } = t.context;
|
||||
const freePlanFeature = await feature.get('free_plan_v1');
|
||||
|
||||
const newFreePlanFeature = await feature.upsert('free_plan_v1', {
|
||||
...freePlanFeature.configs,
|
||||
memberLimit: 10,
|
||||
});
|
||||
const newFreePlanFeature = await feature.upsert(
|
||||
'free_plan_v1',
|
||||
{
|
||||
...freePlanFeature.configs,
|
||||
memberLimit: 10,
|
||||
},
|
||||
FeatureType.Quota,
|
||||
1
|
||||
);
|
||||
|
||||
t.deepEqual(newFreePlanFeature.configs, {
|
||||
...freePlanFeature.configs,
|
||||
@@ -113,7 +122,10 @@ test('should update feature', async t => {
|
||||
|
||||
test('should throw if feature config is invalid when updating', async t => {
|
||||
const { feature } = t.context;
|
||||
await t.throwsAsync(feature.upsert('free_plan_v1', {} as any), {
|
||||
message: 'Invalid feature config for free_plan_v1',
|
||||
});
|
||||
await t.throwsAsync(
|
||||
feature.upsert('free_plan_v1', {} as any, FeatureType.Quota, 1),
|
||||
{
|
||||
message: 'Invalid feature config for free_plan_v1',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { TestingModule } from '@nestjs/testing';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import ava, { TestFn } from 'ava';
|
||||
|
||||
@@ -8,7 +7,7 @@ import { PublicPageMode } from '../../models/common';
|
||||
import { PageModel } from '../../models/page';
|
||||
import { type User, UserModel } from '../../models/user';
|
||||
import { type Workspace, WorkspaceModel } from '../../models/workspace';
|
||||
import { createTestingModule, initTestingDB } from '../utils';
|
||||
import { createTestingModule, type TestingModule } from '../utils';
|
||||
|
||||
interface Context {
|
||||
config: Config;
|
||||
@@ -36,7 +35,7 @@ let user: User;
|
||||
let workspace: Workspace;
|
||||
|
||||
test.beforeEach(async t => {
|
||||
await initTestingDB(t.context.db);
|
||||
await t.context.module.initTestingDB();
|
||||
user = await t.context.user.create({
|
||||
email: 'test@affine.pro',
|
||||
});
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { TestingModule } from '@nestjs/testing';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import ava, { TestFn } from 'ava';
|
||||
|
||||
import { Config } from '../../base/config';
|
||||
import { SessionModel } from '../../models/session';
|
||||
import { UserModel } from '../../models/user';
|
||||
import { createTestingModule, initTestingDB } from '../utils';
|
||||
import { createTestingModule, type TestingModule } from '../utils';
|
||||
|
||||
interface Context {
|
||||
config: Config;
|
||||
@@ -28,7 +27,7 @@ test.before(async t => {
|
||||
});
|
||||
|
||||
test.beforeEach(async t => {
|
||||
await initTestingDB(t.context.db);
|
||||
await t.context.module.initTestingDB();
|
||||
});
|
||||
|
||||
test.after(async t => {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { TestingModule } from '@nestjs/testing';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import ava, { TestFn } from 'ava';
|
||||
import Sinon from 'sinon';
|
||||
@@ -7,7 +6,7 @@ import { EmailAlreadyUsed, EventBus } from '../../base';
|
||||
import { WorkspaceRole } from '../../core/permission';
|
||||
import { UserModel } from '../../models/user';
|
||||
import { WorkspaceMemberStatus } from '../../models/workspace';
|
||||
import { createTestingModule, initTestingDB } from '../utils';
|
||||
import { createTestingModule, type TestingModule } from '../utils';
|
||||
|
||||
interface Context {
|
||||
module: TestingModule;
|
||||
@@ -24,7 +23,7 @@ test.before(async t => {
|
||||
});
|
||||
|
||||
test.beforeEach(async t => {
|
||||
await initTestingDB(t.context.module.get(PrismaClient));
|
||||
await t.context.module.initTestingDB();
|
||||
});
|
||||
|
||||
test.after(async t => {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { TestingModule } from '@nestjs/testing';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import ava, { TestFn } from 'ava';
|
||||
|
||||
@@ -6,7 +5,7 @@ import {
|
||||
TokenType,
|
||||
VerificationTokenModel,
|
||||
} from '../../models/verification-token';
|
||||
import { createTestingModule, initTestingDB } from '../utils';
|
||||
import { createTestingModule, type TestingModule } from '../utils';
|
||||
|
||||
interface Context {
|
||||
module: TestingModule;
|
||||
@@ -25,7 +24,7 @@ test.before(async t => {
|
||||
});
|
||||
|
||||
test.beforeEach(async t => {
|
||||
await initTestingDB(t.context.db);
|
||||
await t.context.module.initTestingDB();
|
||||
});
|
||||
|
||||
test.after(async t => {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { TestingModule } from '@nestjs/testing';
|
||||
import { PrismaClient, WorkspaceMemberStatus } from '@prisma/client';
|
||||
import ava, { TestFn } from 'ava';
|
||||
import Sinon from 'sinon';
|
||||
@@ -7,7 +6,7 @@ import { Config, EventBus } from '../../base';
|
||||
import { WorkspaceRole } from '../../core/permission';
|
||||
import { UserModel } from '../../models/user';
|
||||
import { WorkspaceModel } from '../../models/workspace';
|
||||
import { createTestingModule, initTestingDB } from '../utils';
|
||||
import { createTestingModule, type TestingModule } from '../utils';
|
||||
|
||||
interface Context {
|
||||
config: Config;
|
||||
@@ -29,7 +28,7 @@ test.before(async t => {
|
||||
});
|
||||
|
||||
test.beforeEach(async t => {
|
||||
await initTestingDB(t.context.db);
|
||||
await t.context.module.initTestingDB();
|
||||
});
|
||||
|
||||
test.after(async t => {
|
||||
|
||||
Reference in New Issue
Block a user