feat(server): user feature model (#9843)

close CLOUD-108
This commit is contained in:
forehalo
2025-01-22 10:38:04 +00:00
parent 994d758c07
commit f8a515e89a
7 changed files with 580 additions and 39 deletions

View File

@@ -0,0 +1,95 @@
import { TestingModule } from '@nestjs/testing';
import { PrismaClient, User } from '@prisma/client';
import ava, { TestFn } from 'ava';
import { UserFeatureModel, UserModel } from '../../models';
import { createTestingModule, initTestingDB } from '../utils';
interface Context {
module: TestingModule;
model: UserFeatureModel;
u1: User;
}
const test = ava as TestFn<Context>;
test.before(async t => {
const module = await createTestingModule({});
t.context.model = module.get(UserFeatureModel);
t.context.module = module;
});
test.beforeEach(async t => {
await initTestingDB(t.context.module.get(PrismaClient));
t.context.u1 = await t.context.module.get(UserModel).create({
email: 'u1@affine.pro',
registered: true,
});
});
test.after(async t => {
await t.context.module.close();
});
test('should get null if user feature not found', async t => {
const { model, u1 } = t.context;
const userFeature = await model.get(u1.id, 'ai_early_access');
t.is(userFeature, null);
});
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');
});
test('should list user features', async t => {
const { model, u1 } = t.context;
t.like(await model.list(u1.id), ['free_plan_v1']);
});
test('should directly test user feature existence', async t => {
const { model, u1 } = t.context;
t.true(await model.has(u1.id, 'free_plan_v1'));
t.false(await model.has(u1.id, 'ai_early_access'));
});
test('should add user feature', async t => {
const { model, u1 } = t.context;
await model.add(u1.id, 'unlimited_copilot', 'test');
t.true(await model.has(u1.id, 'unlimited_copilot'));
t.true((await model.list(u1.id)).includes('unlimited_copilot'));
});
test('should not add existing user feature', async t => {
const { model, u1 } = t.context;
await model.add(u1.id, 'free_plan_v1', 'test');
await model.add(u1.id, 'free_plan_v1', 'test');
t.like(await model.list(u1.id), ['free_plan_v1']);
});
test('should remove user feature', async t => {
const { model, u1 } = t.context;
await model.remove(u1.id, 'free_plan_v1');
t.false(await model.has(u1.id, 'free_plan_v1'));
t.false((await model.list(u1.id)).includes('free_plan_v1'));
});
test('should switch user feature', async t => {
const { model, u1 } = t.context;
await model.switch(u1.id, 'free_plan_v1', 'pro_plan_v1', 'test');
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'));
});

View File

@@ -0,0 +1,127 @@
import { TestingModule } from '@nestjs/testing';
import { PrismaClient, Workspace } from '@prisma/client';
import ava, { TestFn } from 'ava';
import { UserModel, WorkspaceFeatureModel, WorkspaceModel } from '../../models';
import { createTestingModule, initTestingDB } from '../utils';
interface Context {
module: TestingModule;
model: WorkspaceFeatureModel;
ws: Workspace;
}
const test = ava as TestFn<Context>;
test.before(async t => {
const module = await createTestingModule({});
t.context.model = module.get(WorkspaceFeatureModel);
t.context.module = module;
});
test.beforeEach(async t => {
await initTestingDB(t.context.module.get(PrismaClient));
const u1 = await t.context.module.get(UserModel).create({
email: 'u1@affine.pro',
registered: true,
});
t.context.ws = await t.context.module.get(WorkspaceModel).create(u1.id);
});
test.after(async t => {
await t.context.module.close();
});
test('should get null if workspace feature not found', async t => {
const { model, ws } = t.context;
const userFeature = await model.get(ws.id, 'unlimited_workspace');
t.is(userFeature, null);
});
test('should directly test workspace feature existence', async t => {
const { model, ws } = t.context;
t.false(await model.has(ws.id, 'unlimited_workspace'));
});
test('should list empty workspace features', async t => {
const { model, ws } = t.context;
t.deepEqual(await model.list(ws.id), []);
});
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,
'unlimited_workspace'
);
t.true(await model.has(ws.id, 'unlimited_workspace'));
t.true((await model.list(ws.id)).includes('unlimited_workspace'));
});
test('should add workspace feature with overrides', async t => {
const { model, ws } = t.context;
await model.add(ws.id, 'team_plan_v1', 'test');
const f1 = await model.get(ws.id, 'team_plan_v1');
await model.add(ws.id, 'team_plan_v1', 'test', { memberLimit: 100 });
const f2 = await model.get(ws.id, 'team_plan_v1');
t.not(f1!.configs.memberLimit, f2!.configs.memberLimit);
t.is(f2!.configs.memberLimit, 100);
});
test('should not add existing workspace feature', async t => {
const { model, ws } = t.context;
await model.add(ws.id, 'team_plan_v1', 'test');
await model.add(ws.id, 'team_plan_v1', 'test');
t.like(await model.list(ws.id), ['team_plan_v1']);
});
test('should replace existing workspace if overrides updated', async t => {
const { model, ws } = t.context;
await model.add(ws.id, 'team_plan_v1', 'test', { memberLimit: 10 });
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);
});
test('should remove workspace feature', async t => {
const { model, ws } = t.context;
await model.add(ws.id, 'team_plan_v1', 'test');
await model.remove(ws.id, 'team_plan_v1');
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);
});