UNPKG

@speckle/shared

Version:

Shared code between various Speckle JS packages

552 lines (512 loc) 15.9 kB
import { describe, expect, it } from 'vitest' import { OverridesOf } from '../../tests/helpers/types.js' import { ensureCanAccessSavedViewFragment, ensureCanAccessSavedViewGroupFragment, WriteTypes } from './savedViews.js' import { getEnvFake, getProjectFake, getSavedViewFake, getSavedViewGroupFake, getWorkspaceFake, getWorkspacePlanFake } from '../../tests/fakes.js' import { SavedViewVisibility } from '../domain/savedViews/types.js' import { Roles } from '../../core/constants.js' import { ProjectNotEnoughPermissionsError, SavedViewGroupNotFoundError, SavedViewNoAccessError, SavedViewNotFoundError, UngroupedSavedViewGroupLockError, WorkspaceNoAccessError } from '../domain/authErrors.js' import { nanoid } from 'nanoid' import { ProjectVisibility } from '../domain/projects/types.js' const userId = 'user-id' const savedViewId = 'saved-view-id' const projectId = 'project-id' const workspaceId = 'workspace-id' const savedViewGroupId = 'saved-view-group-id' describe('ensureCanAccessSavedViewFragment', () => { const buildSUT = (overrides?: OverridesOf<typeof ensureCanAccessSavedViewFragment>) => ensureCanAccessSavedViewFragment({ getSavedView: getSavedViewFake({ id: savedViewId, projectId, visibility: SavedViewVisibility.public }), getProject: getProjectFake({ id: projectId, workspaceId: null }), getProjectRole: async () => Roles.Stream.Contributor, getEnv: getEnvFake({ FF_WORKSPACES_MODULE_ENABLED: true, FF_SAVED_VIEWS_ENABLED: true }), getServerRole: async () => Roles.Server.User, getWorkspace: async () => null, getWorkspacePlan: async () => null, getWorkspaceRole: async () => null, getWorkspaceSsoProvider: async () => null, getWorkspaceSsoSession: async () => null, getAdminOverrideEnabled: async () => false, ...overrides }) it.each(<const>['read', ...Object.values(WriteTypes)])( 'fails when not workspaced project (%s)', async (access) => { const sut = buildSUT() const result = await sut({ userId, projectId, savedViewId, access }) expect(result).toBeAuthErrorResult({ code: WorkspaceNoAccessError.code }) } ) describe('w/ workspaced project', () => { const buildWorkspaceSUT = ( overrides?: OverridesOf<typeof ensureCanAccessSavedViewFragment> ) => buildSUT({ getProject: getProjectFake({ id: projectId, workspaceId }), getWorkspace: getWorkspaceFake({ id: workspaceId }), getWorkspacePlan: getWorkspacePlanFake({ name: 'pro' }), getWorkspaceRole: async () => Roles.Workspace.Member, getWorkspaceSsoProvider: async () => null, getWorkspaceSsoSession: async () => null, ...overrides }) it.each(<const>[ { author: 'author', success: 'succeeds' }, { author: 'not author', success: 'succeeds' }, { author: 'not author and view is private', success: 'fails', error: SavedViewNoAccessError.code } ])( '$success if asking for read access (as $author)', async ({ author, success, error }) => { const isAuthor = author.startsWith('author') const isPrivate = author.includes('view is private') const expectSuccess = success === 'succeeds' const sut = buildWorkspaceSUT({ getSavedView: getSavedViewFake({ id: savedViewId, projectId, visibility: isPrivate ? SavedViewVisibility.authorOnly : SavedViewVisibility.public, authorId: isAuthor ? userId : nanoid() }) }) const result = await sut({ userId, projectId, savedViewId, access: 'read' }) if (expectSuccess) { expect(result).toBeAuthOKResult() } else { expect(result).toBeAuthErrorResult({ code: error }) } } ) it('succeeds if asking for read access to public projects public view, even if not a part of the project or workspace', async () => { const sut = buildWorkspaceSUT({ getSavedView: getSavedViewFake({ id: savedViewId, projectId, visibility: SavedViewVisibility.public, authorId: userId }), getProject: getProjectFake({ id: projectId, workspaceId, visibility: ProjectVisibility.Public }), getProjectRole: async () => null, getWorkspaceRole: async () => null }) const result = await sut({ userId, projectId, savedViewId, access: 'read' }) expect(result).toBeAuthOKResult() }) it.each(<const>[ { author: 'author', success: 'succeeds', access: WriteTypes.UpdateGeneral }, { author: 'author', success: 'succeeds', access: WriteTypes.MoveView }, { author: 'author', success: 'succeeds', access: WriteTypes.EditTitle }, { author: 'author', success: 'succeeds', access: WriteTypes.EditDescription }, { author: 'not author', success: 'fails', error: SavedViewNoAccessError.code, access: WriteTypes.UpdateGeneral }, { author: 'not author', success: 'succeeds', error: SavedViewNoAccessError.code, access: WriteTypes.MoveView }, { author: 'not author', success: 'succeeds', error: SavedViewNoAccessError.code, access: WriteTypes.EditTitle }, { author: 'not author', success: 'succeeds', error: SavedViewNoAccessError.code, access: WriteTypes.EditDescription }, { author: 'author but no longer contributor', success: 'fails', error: ProjectNotEnoughPermissionsError.code, access: WriteTypes.UpdateGeneral }, { author: 'author but no longer contributor', success: 'fails', error: ProjectNotEnoughPermissionsError.code, access: WriteTypes.MoveView }, { author: 'author but no longer contributor', success: 'fails', error: ProjectNotEnoughPermissionsError.code, access: WriteTypes.EditTitle }, { author: 'author but no longer contributor', success: 'fails', error: ProjectNotEnoughPermissionsError.code, access: WriteTypes.EditDescription }, { author: 'not author but is workspace admin', success: 'fails', error: SavedViewNoAccessError.code, access: WriteTypes.UpdateGeneral }, { author: 'not author but is workspace admin', success: 'succeeds', error: SavedViewNoAccessError.code, access: WriteTypes.MoveView }, { author: 'not author but is workspace admin', success: 'succeeds', error: SavedViewNoAccessError.code, access: WriteTypes.EditTitle }, { author: 'not author but is workspace admin', success: 'succeeds', error: SavedViewNoAccessError.code, access: WriteTypes.EditDescription }, // Home view: { author: 'author', success: 'succeeds', access: WriteTypes.SetHomeView }, { author: 'not author', success: 'succeeds', error: SavedViewNoAccessError.code, access: WriteTypes.SetHomeView }, { author: 'author but no longer contributor', success: 'fails', error: ProjectNotEnoughPermissionsError.code, access: WriteTypes.SetHomeView }, { author: 'not author but is workspace admin', success: 'succeeds', error: SavedViewNoAccessError.code, access: WriteTypes.SetHomeView } ])( '$success if asking for $access type write access to private (as $author)', async ({ author, success, error, access }) => { const isAuthor = author.startsWith('author') const isNotContributor = author.includes('no longer contributor') const isWorkspaceAdmin = author.includes('is workspace admin') const expectSuccess = success === 'succeeds' const sut = buildWorkspaceSUT({ getSavedView: getSavedViewFake({ id: savedViewId, projectId, visibility: SavedViewVisibility.public, authorId: isAuthor ? userId : nanoid() }), getProjectRole: async () => isNotContributor ? Roles.Stream.Reviewer : Roles.Stream.Contributor, getWorkspaceRole: async () => isWorkspaceAdmin ? Roles.Workspace.Admin : Roles.Workspace.Member }) const result = await sut({ userId, projectId, savedViewId, access }) if (expectSuccess) { expect(result).toBeAuthOKResult() } else { expect(result).toBeAuthErrorResult({ code: error }) } } ) it.each(<const>['read', ...Object.values(WriteTypes)])( 'succeeds to %s even on free plan', async (access) => { const sut = buildWorkspaceSUT({ getWorkspacePlan: getWorkspacePlanFake({ name: 'free' }), getSavedView: getSavedViewFake({ id: savedViewId, projectId, visibility: SavedViewVisibility.public, authorId: userId }) }) const result = await sut({ userId, projectId, savedViewId, access }) expect(result).toBeAuthOKResult() } ) it.each(<const>['read', ...Object.values(WriteTypes)])( 'fails if view doesnt exist (%s)', async (access) => { const sut = buildWorkspaceSUT({ getSavedView: async () => null }) const result = await sut({ userId, projectId, savedViewId, access }) expect(result).toBeAuthErrorResult({ code: SavedViewNotFoundError.code }) } ) it.each(<const>['read', ...Object.values(WriteTypes)])( `doesn't fail if view doesnt exist and allowNonExistent set (%s)`, async (access) => { const sut = buildWorkspaceSUT({ getSavedView: async () => null }) const result = await sut({ userId, projectId, savedViewId, access, allowNonExistent: true }) expect(result).toBeAuthOKResult() } ) }) }) describe('ensureCanAccessSavedViewGroupFragment', () => { const buildSUT = ( overrides?: OverridesOf<typeof ensureCanAccessSavedViewGroupFragment> ) => ensureCanAccessSavedViewGroupFragment({ getSavedViewGroup: getSavedViewGroupFake({ projectId, id: savedViewGroupId }), getProject: getProjectFake({ id: projectId, workspaceId: null }), getProjectRole: async () => Roles.Stream.Contributor, getEnv: getEnvFake({ FF_WORKSPACES_MODULE_ENABLED: true, FF_SAVED_VIEWS_ENABLED: true }), getServerRole: async () => Roles.Server.User, getWorkspace: async () => null, getWorkspacePlan: async () => null, getWorkspaceRole: async () => null, getWorkspaceSsoProvider: async () => null, getWorkspaceSsoSession: async () => null, ...overrides }) it.each(<const>['read', 'write'])( 'fails when not workspaced project (%s)', async (access) => { const sut = buildSUT() const result = await sut({ userId, projectId, savedViewGroupId, access }) expect(result).toBeAuthErrorResult({ code: WorkspaceNoAccessError.code }) } ) describe('w/ workspaced project', () => { const buildWorkspaceSUT = ( overrides?: OverridesOf<typeof ensureCanAccessSavedViewGroupFragment> ) => buildSUT({ getProject: getProjectFake({ id: projectId, workspaceId }), getWorkspace: getWorkspaceFake({ id: workspaceId }), getWorkspacePlan: getWorkspacePlanFake({ name: 'pro' }), getWorkspaceRole: async () => Roles.Workspace.Member, getWorkspaceSsoProvider: async () => null, getWorkspaceSsoSession: async () => null, ...overrides }) it.each(<const>[ { author: 'author', success: 'succeeds' }, { author: 'not author', success: 'succeeds' } ])('$success if asking for read access (as $author)', async ({ author }) => { const isAuthor = author.startsWith('author') const sut = buildWorkspaceSUT({ getProjectRole: async () => null, getSavedViewGroup: getSavedViewGroupFake({ id: savedViewGroupId, projectId, authorId: isAuthor ? userId : nanoid() }) }) const result = await sut({ userId, projectId, savedViewGroupId, access: 'read' }) expect(result).toBeAuthOKResult() }) it.each(<const>[ { author: 'author', success: 'succeeds' }, { author: 'not author', success: 'fails', error: ProjectNotEnoughPermissionsError.code }, { author: 'author but no longer contributor', success: 'fails', error: ProjectNotEnoughPermissionsError.code }, { author: 'not author but is workspace admin', success: 'succeeds' } ])( '$success if asking for write access to private (as $author)', async ({ author, success, error }) => { const isAuthor = author.startsWith('author') const isNotContributor = author.includes('no longer contributor') const isWorkspaceAdmin = author.includes('is workspace admin') const expectSuccess = success === 'succeeds' const sut = buildWorkspaceSUT({ getSavedViewGroup: getSavedViewGroupFake({ id: savedViewId, projectId, authorId: isAuthor ? userId : nanoid() }), getProjectRole: async () => isNotContributor ? Roles.Stream.Reviewer : Roles.Stream.Contributor, getWorkspaceRole: async () => isWorkspaceAdmin ? Roles.Workspace.Admin : Roles.Workspace.Member }) const result = await sut({ userId, projectId, savedViewGroupId, access: 'write' }) if (expectSuccess) { expect(result).toBeAuthOKResult() } else { expect(result).toBeAuthErrorResult({ code: error }) } } ) it('fails if writing to default group', async () => { const sut = buildWorkspaceSUT({ getSavedViewGroup: getSavedViewGroupFake({ projectId: 'project-id', id: 'default-XXX' }) }) const result = await sut({ userId: 'user-id', projectId: 'project-id', savedViewGroupId: 'default-XXX', access: 'write' }) expect(result).toBeAuthErrorResult({ code: UngroupedSavedViewGroupLockError.code }) }) it.each(<const>['read', 'write'])( 'fails if view doesnt exist (%s)', async (access) => { const sut = buildWorkspaceSUT({ getSavedViewGroup: async () => null }) const result = await sut({ userId, projectId, savedViewGroupId, access }) expect(result).toBeAuthErrorResult({ code: SavedViewGroupNotFoundError.code }) } ) }) })