2023-09-13 08:11:19 +03:00
|
|
|
import {
|
|
|
|
getCurrentMailMessageCount,
|
|
|
|
getLatestMailMessage,
|
|
|
|
} from '@affine-test/kit/utils/cloud';
|
2023-08-29 13:07:05 +03:00
|
|
|
import type { INestApplication } from '@nestjs/common';
|
|
|
|
import { PrismaClient } from '@prisma/client';
|
2024-03-25 06:55:32 +03:00
|
|
|
import type { TestFn } from 'ava';
|
|
|
|
import ava from 'ava';
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2024-01-14 08:47:56 +03:00
|
|
|
import { AppModule } from '../src/app.module';
|
2024-01-22 10:40:28 +03:00
|
|
|
import { AuthService } from '../src/core/auth/service';
|
2024-01-12 07:18:39 +03:00
|
|
|
import { MailService } from '../src/fundamentals/mailer';
|
2023-08-29 13:07:05 +03:00
|
|
|
import {
|
|
|
|
acceptInviteById,
|
2024-01-11 09:40:55 +03:00
|
|
|
createTestingApp,
|
2023-08-29 13:07:05 +03:00
|
|
|
createWorkspace,
|
|
|
|
getWorkspace,
|
|
|
|
inviteUser,
|
|
|
|
leaveWorkspace,
|
|
|
|
revokeUser,
|
|
|
|
signUp,
|
|
|
|
} from './utils';
|
|
|
|
|
2023-09-09 02:02:01 +03:00
|
|
|
const test = ava as TestFn<{
|
|
|
|
app: INestApplication;
|
|
|
|
client: PrismaClient;
|
|
|
|
auth: AuthService;
|
|
|
|
mail: MailService;
|
|
|
|
}>;
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-09 02:02:01 +03:00
|
|
|
test.beforeEach(async t => {
|
2024-01-11 09:40:55 +03:00
|
|
|
const { app } = await createTestingApp({
|
2023-09-01 22:41:29 +03:00
|
|
|
imports: [AppModule],
|
2024-01-11 09:40:55 +03:00
|
|
|
});
|
2023-09-09 02:02:01 +03:00
|
|
|
t.context.app = app;
|
2024-01-12 07:18:39 +03:00
|
|
|
t.context.client = app.get(PrismaClient);
|
2024-01-11 09:40:55 +03:00
|
|
|
t.context.auth = app.get(AuthService);
|
|
|
|
t.context.mail = app.get(MailService);
|
2023-09-01 22:41:29 +03:00
|
|
|
});
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-11 12:30:39 +03:00
|
|
|
test.afterEach.always(async t => {
|
2023-09-09 02:02:01 +03:00
|
|
|
await t.context.app.close();
|
2023-09-01 22:41:29 +03:00
|
|
|
});
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-01 22:41:29 +03:00
|
|
|
test('should invite a user', async t => {
|
2023-09-09 02:02:01 +03:00
|
|
|
const { app } = t.context;
|
2023-09-01 22:41:29 +03:00
|
|
|
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
|
|
|
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
|
|
|
|
|
|
|
const workspace = await createWorkspace(app, u1.token.token);
|
|
|
|
|
|
|
|
const invite = await inviteUser(
|
|
|
|
app,
|
|
|
|
u1.token.token,
|
|
|
|
workspace.id,
|
|
|
|
u2.email,
|
|
|
|
'Admin'
|
|
|
|
);
|
2023-09-05 11:01:45 +03:00
|
|
|
t.truthy(invite, 'failed to invite user');
|
2023-09-01 22:41:29 +03:00
|
|
|
});
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-01 22:41:29 +03:00
|
|
|
test('should leave a workspace', async t => {
|
2023-09-09 02:02:01 +03:00
|
|
|
const { app } = t.context;
|
2023-09-01 22:41:29 +03:00
|
|
|
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
|
|
|
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-01 22:41:29 +03:00
|
|
|
const workspace = await createWorkspace(app, u1.token.token);
|
2023-11-06 06:49:39 +03:00
|
|
|
const id = await inviteUser(
|
|
|
|
app,
|
|
|
|
u1.token.token,
|
|
|
|
workspace.id,
|
|
|
|
u2.email,
|
|
|
|
'Admin'
|
|
|
|
);
|
|
|
|
await acceptInviteById(app, workspace.id, id, false);
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-01 22:41:29 +03:00
|
|
|
const leave = await leaveWorkspace(app, u2.token.token, workspace.id);
|
2023-09-07 08:08:23 +03:00
|
|
|
|
|
|
|
t.pass();
|
2023-09-05 11:01:45 +03:00
|
|
|
t.true(leave, 'failed to leave workspace');
|
2023-09-01 22:41:29 +03:00
|
|
|
});
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-01 22:41:29 +03:00
|
|
|
test('should revoke a user', async t => {
|
2023-09-09 02:02:01 +03:00
|
|
|
const { app } = t.context;
|
2023-09-01 22:41:29 +03:00
|
|
|
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
|
|
|
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-01 22:41:29 +03:00
|
|
|
const workspace = await createWorkspace(app, u1.token.token);
|
|
|
|
await inviteUser(app, u1.token.token, workspace.id, u2.email, 'Admin');
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-01 22:41:29 +03:00
|
|
|
const currWorkspace = await getWorkspace(app, u1.token.token, workspace.id);
|
2023-09-05 11:01:45 +03:00
|
|
|
t.is(currWorkspace.members.length, 2, 'failed to invite user');
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-01 22:41:29 +03:00
|
|
|
const revoke = await revokeUser(app, u1.token.token, workspace.id, u2.id);
|
2023-09-05 11:01:45 +03:00
|
|
|
t.true(revoke, 'failed to revoke user');
|
2023-09-01 22:41:29 +03:00
|
|
|
});
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-01 22:41:29 +03:00
|
|
|
test('should create user if not exist', async t => {
|
2023-09-09 02:02:01 +03:00
|
|
|
const { app, auth } = t.context;
|
2023-09-01 22:41:29 +03:00
|
|
|
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-01 22:41:29 +03:00
|
|
|
const workspace = await createWorkspace(app, u1.token.token);
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-01 22:41:29 +03:00
|
|
|
await inviteUser(app, u1.token.token, workspace.id, 'u2@affine.pro', 'Admin');
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-01 22:41:29 +03:00
|
|
|
const user = await auth.getUserByEmail('u2@affine.pro');
|
2023-09-05 11:01:45 +03:00
|
|
|
t.not(user, undefined, 'failed to create user');
|
2024-03-19 05:16:23 +03:00
|
|
|
t.is(user?.name, 'u2', 'failed to create user');
|
2023-09-01 22:41:29 +03:00
|
|
|
});
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-01 22:41:29 +03:00
|
|
|
test('should invite a user by link', async t => {
|
2023-09-09 02:02:01 +03:00
|
|
|
const { app } = t.context;
|
2023-09-01 22:41:29 +03:00
|
|
|
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
|
|
|
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
|
|
|
|
|
|
|
const workspace = await createWorkspace(app, u1.token.token);
|
|
|
|
|
|
|
|
const invite = await inviteUser(
|
|
|
|
app,
|
|
|
|
u1.token.token,
|
|
|
|
workspace.id,
|
|
|
|
u2.email,
|
|
|
|
'Admin'
|
|
|
|
);
|
|
|
|
|
|
|
|
const accept = await acceptInviteById(app, workspace.id, invite);
|
2023-09-05 11:01:45 +03:00
|
|
|
t.true(accept, 'failed to accept invite');
|
2023-09-01 22:41:29 +03:00
|
|
|
|
|
|
|
const invite1 = await inviteUser(
|
|
|
|
app,
|
|
|
|
u1.token.token,
|
|
|
|
workspace.id,
|
|
|
|
u2.email,
|
|
|
|
'Admin'
|
|
|
|
);
|
|
|
|
|
2023-09-05 11:01:45 +03:00
|
|
|
t.is(invite, invite1, 'repeat the invitation must return same id');
|
2023-09-01 22:41:29 +03:00
|
|
|
|
|
|
|
const currWorkspace = await getWorkspace(app, u1.token.token, workspace.id);
|
|
|
|
const currMember = currWorkspace.members.find(u => u.email === u2.email);
|
2023-09-05 11:01:45 +03:00
|
|
|
t.not(currMember, undefined, 'failed to invite user');
|
|
|
|
t.is(currMember?.inviteId, invite, 'failed to check invite id');
|
2023-09-01 22:41:29 +03:00
|
|
|
});
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-07 08:08:23 +03:00
|
|
|
test('should send email', async t => {
|
2023-09-09 02:02:01 +03:00
|
|
|
const { mail, app } = t.context;
|
2023-09-01 22:41:29 +03:00
|
|
|
if (mail.hasConfigured()) {
|
|
|
|
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
|
|
|
const u2 = await signUp(app, 'test', 'production@toeverything.info', '1');
|
2023-08-29 13:07:05 +03:00
|
|
|
|
2023-09-01 22:41:29 +03:00
|
|
|
const workspace = await createWorkspace(app, u1.token.token);
|
2023-09-07 08:08:23 +03:00
|
|
|
const primitiveMailCount = await getCurrentMailMessageCount();
|
|
|
|
|
|
|
|
const invite = await inviteUser(
|
2023-08-29 13:07:05 +03:00
|
|
|
app,
|
|
|
|
u1.token.token,
|
|
|
|
workspace.id,
|
|
|
|
u2.email,
|
2023-09-01 22:41:29 +03:00
|
|
|
'Admin',
|
|
|
|
true
|
2023-08-29 13:07:05 +03:00
|
|
|
);
|
2023-09-07 08:08:23 +03:00
|
|
|
|
|
|
|
const afterInviteMailCount = await getCurrentMailMessageCount();
|
|
|
|
t.is(
|
|
|
|
primitiveMailCount + 1,
|
|
|
|
afterInviteMailCount,
|
|
|
|
'failed to send invite email'
|
|
|
|
);
|
|
|
|
const inviteEmailContent = await getLatestMailMessage();
|
|
|
|
|
|
|
|
t.not(
|
2023-09-15 10:34:14 +03:00
|
|
|
inviteEmailContent.To.find((item: any) => {
|
2023-09-07 08:08:23 +03:00
|
|
|
return item.Mailbox === 'production';
|
|
|
|
}),
|
|
|
|
undefined,
|
|
|
|
'invite email address was incorrectly sent'
|
|
|
|
);
|
|
|
|
|
|
|
|
const accept = await acceptInviteById(app, workspace.id, invite, true);
|
|
|
|
t.true(accept, 'failed to accept invite');
|
|
|
|
|
|
|
|
const afterAcceptMailCount = await getCurrentMailMessageCount();
|
|
|
|
t.is(
|
|
|
|
afterInviteMailCount + 1,
|
|
|
|
afterAcceptMailCount,
|
|
|
|
'failed to send accepted email to owner'
|
|
|
|
);
|
|
|
|
const acceptEmailContent = await getLatestMailMessage();
|
|
|
|
t.not(
|
2023-09-15 10:34:14 +03:00
|
|
|
acceptEmailContent.To.find((item: any) => {
|
2023-09-07 08:08:23 +03:00
|
|
|
return item.Mailbox === 'u1';
|
|
|
|
}),
|
|
|
|
undefined,
|
|
|
|
'accept email address was incorrectly sent'
|
|
|
|
);
|
|
|
|
|
|
|
|
await leaveWorkspace(app, u2.token.token, workspace.id, true);
|
|
|
|
|
|
|
|
const afterLeaveMailCount = await getCurrentMailMessageCount();
|
|
|
|
t.is(
|
|
|
|
afterAcceptMailCount + 1,
|
|
|
|
afterLeaveMailCount,
|
|
|
|
'failed to send leave email to owner'
|
|
|
|
);
|
|
|
|
const leaveEmailContent = await getLatestMailMessage();
|
|
|
|
t.not(
|
2023-09-15 10:34:14 +03:00
|
|
|
leaveEmailContent.To.find((item: any) => {
|
2023-09-07 08:08:23 +03:00
|
|
|
return item.Mailbox === 'u1';
|
|
|
|
}),
|
|
|
|
undefined,
|
|
|
|
'leave email address was incorrectly sent'
|
|
|
|
);
|
2023-09-01 22:41:29 +03:00
|
|
|
}
|
|
|
|
t.pass();
|
2023-08-29 13:07:05 +03:00
|
|
|
});
|
2023-09-12 06:37:59 +03:00
|
|
|
|
|
|
|
test('should support pagination for member', async t => {
|
|
|
|
const { app } = t.context;
|
|
|
|
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
|
|
|
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
|
|
|
const u3 = await signUp(app, 'u3', 'u3@affine.pro', '1');
|
|
|
|
|
|
|
|
const workspace = await createWorkspace(app, u1.token.token);
|
2023-11-06 06:49:39 +03:00
|
|
|
const invite1 = await inviteUser(
|
|
|
|
app,
|
|
|
|
u1.token.token,
|
|
|
|
workspace.id,
|
|
|
|
u2.email,
|
|
|
|
'Admin'
|
|
|
|
);
|
|
|
|
const invite2 = await inviteUser(
|
|
|
|
app,
|
|
|
|
u1.token.token,
|
|
|
|
workspace.id,
|
|
|
|
u3.email,
|
|
|
|
'Admin'
|
|
|
|
);
|
2023-09-12 06:37:59 +03:00
|
|
|
|
2023-11-06 06:49:39 +03:00
|
|
|
await acceptInviteById(app, workspace.id, invite1, false);
|
|
|
|
await acceptInviteById(app, workspace.id, invite2, false);
|
2023-09-12 06:37:59 +03:00
|
|
|
|
|
|
|
const firstPageWorkspace = await getWorkspace(
|
|
|
|
app,
|
|
|
|
u1.token.token,
|
|
|
|
workspace.id,
|
|
|
|
0,
|
|
|
|
2
|
|
|
|
);
|
|
|
|
t.is(firstPageWorkspace.members.length, 2, 'failed to check invite id');
|
|
|
|
const secondPageWorkspace = await getWorkspace(
|
|
|
|
app,
|
|
|
|
u1.token.token,
|
|
|
|
workspace.id,
|
|
|
|
2,
|
|
|
|
2
|
|
|
|
);
|
|
|
|
t.is(secondPageWorkspace.members.length, 1, 'failed to check invite id');
|
|
|
|
});
|
2024-03-19 05:16:23 +03:00
|
|
|
|
|
|
|
test('should limit member count correctly', async t => {
|
|
|
|
const { app } = t.context;
|
|
|
|
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
|
|
const workspace = await createWorkspace(app, u1.token.token);
|
|
|
|
await Promise.allSettled(
|
|
|
|
Array.from({ length: 10 }).map(async (_, i) =>
|
|
|
|
inviteUser(
|
|
|
|
app,
|
|
|
|
u1.token.token,
|
|
|
|
workspace.id,
|
|
|
|
`u${i}@affine.pro`,
|
|
|
|
'Admin'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
const ws = await getWorkspace(app, u1.token.token, workspace.id);
|
|
|
|
t.assert(ws.members.length <= 3, 'failed to check member list');
|
|
|
|
}
|
|
|
|
});
|