Files
speckle-server/packages/server/modules/workspaces/tests/integration/subs.graph.spec.ts
T
Kristaps Fabians Geikins 4b6e7af19d feat(fe2): scope comment mentions to parent project collaborators (#3635)
* backend done

* get rid of email mention

* project scoped mentions work

* lint fix

* better user sanitization safety

* tests fix
2024-12-05 13:33:05 +02:00

380 lines
12 KiB
TypeScript

import { db } from '@/db/knex'
import { ServerInvites } from '@/modules/core/dbSchema'
import { getEventBus } from '@/modules/shared/services/eventBus'
import { getDefaultRegionFactory } from '@/modules/workspaces/repositories/regions'
import { getWorkspaceSsoProviderRecordFactory } from '@/modules/workspaces/repositories/sso'
import {
getWorkspaceBySlugFactory,
getWorkspaceWithDomainsFactory,
upsertWorkspaceFactory
} from '@/modules/workspaces/repositories/workspaces'
import {
updateWorkspaceFactory,
validateSlugFactory
} from '@/modules/workspaces/services/management'
import { createWorkspaceProjectFactory } from '@/modules/workspaces/services/projects'
import {
BasicTestWorkspace,
createTestWorkspace,
unassignFromWorkspace
} from '@/modules/workspaces/tests/helpers/creation'
import {
buildInvitesGraphqlOperations,
TestInvitesGraphQLOperations
} from '@/modules/workspaces/tests/helpers/invites'
import { itEach } from '@/test/assertionHelper'
import { BasicTestUser, createTestUser } from '@/test/authHelper'
import {
OnWorkspaceProjectsUpdatedDocument,
OnWorkspaceUpdatedDocument,
WorkspaceProjectsUpdatedMessageType
} from '@/test/graphql/generated/graphql'
import {
testApolloServer,
TestApolloServer,
TestApolloSubscriptionClient,
testApolloSubscriptionServer,
TestApolloSubscriptionServer
} from '@/test/graphqlHelper'
import { beforeEachContext, truncateTables } from '@/test/hooks'
import { captureCreatedInvite } from '@/test/speckle-helpers/inviteHelper'
import {
getMainTestRegionKey,
isMultiRegionTestMode,
waitForRegionUsers
} from '@/test/speckle-helpers/regions'
import { faker } from '@faker-js/faker'
import { expect } from 'chai'
enum WorkspaceIdentification {
WithId = 'with id',
WithSlug = 'with slug'
}
const createWorkspaceProject = createWorkspaceProjectFactory({
getDefaultRegion: getDefaultRegionFactory({ db })
})
const updateWorkspace = updateWorkspaceFactory({
validateSlug: validateSlugFactory({
getWorkspaceBySlug: getWorkspaceBySlugFactory({ db })
}),
getWorkspace: getWorkspaceWithDomainsFactory({ db }),
getWorkspaceSsoProviderRecord: getWorkspaceSsoProviderRecordFactory({ db }),
upsertWorkspace: upsertWorkspaceFactory({ db }),
emitWorkspaceEvent: getEventBus().emit
})
describe('Workspace GQL Subscriptions', () => {
let me: BasicTestUser
let otherGuy: BasicTestUser
let subServer: TestApolloSubscriptionServer
let meSubClient: TestApolloSubscriptionClient
let apollo: TestApolloServer
let invitesHelpers: TestInvitesGraphQLOperations
before(async () => {
await beforeEachContext()
me = await createTestUser()
otherGuy = await createTestUser()
subServer = await testApolloSubscriptionServer()
meSubClient = await subServer.buildClient({ authUserId: me.id })
apollo = await testApolloServer({ authUserId: me.id })
invitesHelpers = buildInvitesGraphqlOperations({ apollo })
})
after(async () => {
subServer.quit()
})
const modes = [
{ isMultiRegion: false },
...(isMultiRegionTestMode() ? [{ isMultiRegion: true }] : [])
]
modes.forEach(({ isMultiRegion }) => {
describe(`W/${!isMultiRegion ? 'o' : ''} multiregion`, () => {
const myMainWorkspace: BasicTestWorkspace = {
id: '',
ownerId: '',
slug: '',
name: 'My Main Workspace'
}
before(async () => {
await waitForRegionUsers([me, otherGuy])
await createTestWorkspace(myMainWorkspace, me, {
regionKey: isMultiRegion ? getMainTestRegionKey() : undefined
})
})
itEach(
[WorkspaceIdentification.WithId, WorkspaceIdentification.WithSlug],
(idType) => `sub ${idType} and notify when a project is added to the workspace`,
async (idType) => {
const sub = await meSubClient.subscribe(
OnWorkspaceProjectsUpdatedDocument,
{
workspaceId:
idType === WorkspaceIdentification.WithId
? myMainWorkspace.id
: undefined,
workspaceSlug:
idType === WorkspaceIdentification.WithSlug
? myMainWorkspace.slug
: undefined
},
(res) => {
expect(res).to.not.haveGraphQLErrors()
expect(res.data?.workspaceProjectsUpdated.type).to.equal(
WorkspaceProjectsUpdatedMessageType.Added
)
}
)
await meSubClient.waitForReadiness()
await createWorkspaceProject({
input: {
workspaceId: myMainWorkspace.id,
name: 'New Workspace Project ' + faker.number.int()
},
ownerId: me.id
})
await sub.waitForMessage()
expect(sub.getMessages()).to.have.length(1)
}
)
itEach(
[WorkspaceIdentification.WithId, WorkspaceIdentification.WithSlug],
(idType) => `sub ${idType} and notify when a workspace is updated`,
async (idType) => {
const sub = await meSubClient.subscribe(
OnWorkspaceUpdatedDocument,
{
workspaceId:
idType === WorkspaceIdentification.WithId
? myMainWorkspace.id
: undefined,
workspaceSlug:
idType === WorkspaceIdentification.WithSlug
? myMainWorkspace.slug
: undefined
},
(res) => {
expect(res).to.not.haveGraphQLErrors()
expect(res.data?.workspaceUpdated.id).to.equal(myMainWorkspace.id)
expect(res.data?.workspaceUpdated.workspace.slug).to.equal(
myMainWorkspace.slug
)
}
)
await meSubClient.waitForReadiness()
await updateWorkspace({
workspaceId: myMainWorkspace.id,
workspaceInput: {
name: 'Updated Workspace Name'
}
})
await sub.waitForMessage()
expect(sub.getMessages()).to.have.length(1)
}
)
describe('team changes', () => {
const myTeamWorkspace: BasicTestWorkspace = {
id: '',
ownerId: '',
slug: '',
name: 'My Team Workspace'
}
before(async () => {
await createTestWorkspace(myTeamWorkspace, me, {
regionKey: isMultiRegion ? getMainTestRegionKey() : undefined
})
})
afterEach(async () => {
await truncateTables([ServerInvites.name])
await unassignFromWorkspace(myTeamWorkspace, otherGuy)
})
itEach(
[WorkspaceIdentification.WithId, WorkspaceIdentification.WithSlug],
(idType) => `sub ${idType} and notify when a workspace has a new invite`,
async (idType) => {
const sub = await meSubClient.subscribe(
OnWorkspaceUpdatedDocument,
{
workspaceId:
idType === WorkspaceIdentification.WithId
? myMainWorkspace.id
: undefined,
workspaceSlug:
idType === WorkspaceIdentification.WithSlug
? myMainWorkspace.slug
: undefined
},
(res) => {
expect(res).to.not.haveGraphQLErrors()
expect(res.data?.workspaceUpdated.id).to.equal(myMainWorkspace.id)
expect(res.data?.workspaceUpdated.workspace.slug).to.equal(
myMainWorkspace.slug
)
const invite = res.data?.workspaceUpdated.workspace.invitedTeam?.[0]
expect(invite?.user?.id).to.equal(otherGuy.id)
}
)
await meSubClient.waitForReadiness()
// Invite user to workspace
await invitesHelpers.createInvite(
{
workspaceId: myMainWorkspace.id,
input: {
userId: otherGuy.id
}
},
{ assertNoErrors: true }
)
await sub.waitForMessage()
expect(sub.getMessages()).to.have.length(1)
}
)
itEach(
[WorkspaceIdentification.WithId, WorkspaceIdentification.WithSlug],
(idType) => `sub ${idType} and notify when a workspace invite is canceled`,
async (idType) => {
const { id: inviteId } = await captureCreatedInvite(
async () =>
await invitesHelpers.createInvite(
{
workspaceId: myTeamWorkspace.id,
input: {
userId: otherGuy.id
}
},
{ assertNoErrors: true }
)
)
const sub = await meSubClient.subscribe(
OnWorkspaceUpdatedDocument,
{
workspaceId:
idType === WorkspaceIdentification.WithId
? myTeamWorkspace.id
: undefined,
workspaceSlug:
idType === WorkspaceIdentification.WithSlug
? myTeamWorkspace.slug
: undefined
},
(res) => {
expect(res).to.not.haveGraphQLErrors()
expect(res.data?.workspaceUpdated.id).to.equal(myTeamWorkspace.id)
expect(res.data?.workspaceUpdated.workspace.slug).to.equal(
myTeamWorkspace.slug
)
expect(
res.data?.workspaceUpdated.workspace.invitedTeam || []
).to.have.length(0)
}
)
await meSubClient.waitForReadiness()
// Cancel invite
await invitesHelpers.cancelInvite(
{
workspaceId: myTeamWorkspace.id,
inviteId
},
{ assertNoErrors: true }
)
await sub.waitForMessage()
expect(sub.getMessages()).to.have.length(1)
}
)
itEach(
[
{ idType: WorkspaceIdentification.WithId, accept: true },
{ idType: WorkspaceIdentification.WithSlug, accept: true },
{ idType: WorkspaceIdentification.WithId, accept: false },
{ idType: WorkspaceIdentification.WithSlug, accept: false }
],
({ idType, accept }) =>
`sub ${idType} and notify when a workspace invite is ${
accept ? 'accepted' : 'declined'
}`,
async ({ idType, accept }) => {
const { token } = await captureCreatedInvite(
async () =>
await invitesHelpers.createInvite(
{
workspaceId: myTeamWorkspace.id,
input: {
userId: otherGuy.id
}
},
{ assertNoErrors: true }
)
)
const sub = await meSubClient.subscribe(
OnWorkspaceUpdatedDocument,
{
workspaceId:
idType === WorkspaceIdentification.WithId
? myTeamWorkspace.id
: undefined,
workspaceSlug:
idType === WorkspaceIdentification.WithSlug
? myTeamWorkspace.slug
: undefined
},
(res) => {
expect(res).to.not.haveGraphQLErrors()
expect(res.data?.workspaceUpdated.id).to.equal(myTeamWorkspace.id)
expect(res.data?.workspaceUpdated.workspace.slug).to.equal(
myTeamWorkspace.slug
)
expect(
res.data?.workspaceUpdated.workspace.invitedTeam || []
).to.have.length(0)
const team = res.data?.workspaceUpdated.workspace.team.items || []
const newTeammateAdded = !!team.find((t) => t.user.id === otherGuy.id)
expect(newTeammateAdded).to.equal(accept)
}
)
await meSubClient.waitForReadiness()
// Accept invite
await invitesHelpers.useInvite(
{
input: {
token,
accept
}
},
{ assertNoErrors: true, authUserId: otherGuy.id }
)
await sub.waitForMessage()
expect(sub.getMessages()).to.have.length(1)
}
)
})
})
})
})