-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(server): user feature model (#9843)
close CLOUD-108
- Loading branch information
Showing
7 changed files
with
580 additions
and
39 deletions.
There are no files selected for viewing
95 changes: 95 additions & 0 deletions
95
packages/backend/server/src/__tests__/models/feature-user.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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')); | ||
}); |
127 changes: 127 additions & 0 deletions
127
packages/backend/server/src/__tests__/models/feature-workspace.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.