Files
speckle-server/packages/server/modules/core/tests/graph.spec.js
T

1946 lines
67 KiB
JavaScript

/* istanbul ignore file */
const expect = require('chai').expect
const request = require('supertest')
const { beforeEachContext, initializeTestServer } = require(`@/test/hooks`)
const { generateManyObjects } = require(`@/test/helpers`)
const {
createUser,
getUsers,
changeUserRole
} = require('@/modules/core/services/users')
const { createPersonalAccessToken } = require('../services/tokens')
const {
addOrUpdateStreamCollaborator,
removeStreamCollaborator
} = require('@/modules/core/services/streams/streamAccessService')
const { Roles, Scopes } = require('@speckle/shared')
let app
let server
let sendRequest
describe('GraphQL API Core @core-api', () => {
const userA = {
name: 'd1',
email: 'd.1@speckle.systems',
password: 'wowwowwowwowwow'
}
const userB = {
name: 'd2',
email: 'd.2@speckle.systems',
password: 'wowwowwowwowwow'
}
const userC = {
name: 'd3',
email: 'd.3@speckle.systems',
password: 'wowwowwowwowwow'
}
// set up app & two basic users to ping pong permissions around
before(async () => {
;({ app, server } = await beforeEachContext())
;({ sendRequest } = await initializeTestServer(server, app))
userA.id = await createUser(userA)
userA.token = `Bearer ${await createPersonalAccessToken(
userA.id,
'test token user A',
[
Scopes.Server.Setup,
Scopes.Streams.Read,
Scopes.Streams.Write,
Scopes.Users.Read,
Scopes.Users.Email,
Scopes.Tokens.Write,
Scopes.Tokens.Read,
Scopes.Profile.Read,
Scopes.Profile.Email
]
)}`
userB.id = await createUser(userB)
userB.token = `Bearer ${await createPersonalAccessToken(
userB.id,
'test token user B',
[
Scopes.Streams.Read,
Scopes.Streams.Write,
Scopes.Users.Read,
Scopes.Users.Email,
Scopes.Tokens.Write,
Scopes.Tokens.Read,
Scopes.Profile.Read,
Scopes.Profile.Email
]
)}`
userC.id = await createUser(userC)
userC.token = `Bearer ${await createPersonalAccessToken(
userC.id,
'test token user B',
[
Scopes.Streams.Read,
Scopes.Streams.Write,
Scopes.Users.Read,
Scopes.Users.Email,
Scopes.Tokens.Write,
Scopes.Tokens.Read,
Scopes.Profile.Read,
Scopes.Profile.Email
]
)}`
// Prepare API tokens for use in tests
const res1 = await sendRequest(userA.token, {
query:
'mutation { apiTokenCreate(token: {name:"Token 1", scopes: ["streams:read", "users:read", "tokens:read"]}) }'
})
token1 = `Bearer ${res1.body.data.apiTokenCreate}`
const res2 = await sendRequest(userA.token, {
query:
'mutation { apiTokenCreate(token: {name:"Token 1", scopes: ["streams:write", "streams:read", "users:email"]}) }'
})
token2 = `Bearer ${res2.body.data.apiTokenCreate}`
const res3 = await sendRequest(userB.token, {
query:
'mutation { apiTokenCreate(token: {name:"Token 1", scopes: ["streams:write", "streams:read", "users:email"]}) }'
})
token3 = `Bearer ${res3.body.data.apiTokenCreate}`
// Moved stream tests to before() hook, cause other tests depend on these results
const resS1 = await sendRequest(userA.token, {
query:
'mutation { streamCreate(stream: { name: "TS1 (u A) Private", description: "Hello World", isPublic:false } ) }'
})
ts1 = resS1.body.data.streamCreate
const resS2 = await sendRequest(userA.token, {
query:
'mutation { streamCreate(stream: { name: "TS2 (u A)", description: "Hello Darkness", isPublic:true } ) }'
})
ts2 = resS2.body.data.streamCreate
const resS3 = await sendRequest(userB.token, {
query:
'mutation { streamCreate(stream: { name: "TS3 (u B) Private", description: "Hello Pumba", isPublic:false } ) }'
})
ts3 = resS3.body.data.streamCreate
const resS4 = await sendRequest(userB.token, {
query:
'mutation { streamCreate(stream: { name: "TS4 (u B)", description: "Hello Julian", isPublic:true } ) }'
})
ts4 = resS4.body.data.streamCreate
const resS5 = await sendRequest(userB.token, {
query:
'mutation { streamCreate(stream: { name: "TS5 (u B)", description: "Hello King", isPublic:true } ) }'
})
ts5 = resS5.body.data.streamCreate
})
after(async () => {
await server.close()
})
// the stream ids
let ts1
let ts2
let ts3
let ts4
let ts5
let ts6
// some api tokens
let token1
let token2
let token3
// object ids
let objIds
// some commits
const c1 = {}
const c2 = {}
// some branches
let b1 = {}
let b2 = {}
let b3 = {}
let b4 = {}
describe('Mutations', () => {
describe('Users & Api tokens', () => {
it('Should create some api tokens', async () => {
const res1 = await sendRequest(userA.token, {
query:
'mutation { apiTokenCreate(token: {name:"Token 1", scopes: ["streams:read", "users:read", "tokens:read"]}) }'
})
expect(res1).to.be.json
expect(res1.body.errors).to.not.exist
expect(res1.body.data.apiTokenCreate).to.be.a('string')
token1 = `Bearer ${res1.body.data.apiTokenCreate}`
const res2 = await sendRequest(userA.token, {
query:
'mutation { apiTokenCreate(token: {name:"Token 1", scopes: ["streams:write", "streams:read", "users:email"]}) }'
})
token2 = `Bearer ${res2.body.data.apiTokenCreate}`
const res3 = await sendRequest(userB.token, {
query:
'mutation { apiTokenCreate(token: {name:"Token 1", scopes: ["streams:write", "streams:read", "users:email"]}) }'
})
token3 = `Bearer ${res3.body.data.apiTokenCreate}`
})
it('Should revoke an api token that the user owns', async () => {
const res = await sendRequest(userA.token, {
query: `mutation{ apiTokenRevoke(token:"${token2}")}`
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.apiTokenRevoke).to.equal(true)
})
it('Should fail to revoke an api token that I do not own', async () => {
const res = await sendRequest(userA.token, {
query: `mutation{ apiTokenRevoke(token:"${token3}")}`
})
expect(res).to.be.json
expect(res.body.errors).to.exist
})
it('Should fail to create a stream with an invalid scope token', async () => {
// Note: token1 has only stream read access
const res = await sendRequest(token1, {
query:
'mutation { streamCreate(stream: { name: "INVALID TS1 (u A) Private", description: "Hello World", isPublic:false } ) }'
})
expect(res.body.errors).to.exist
})
it('Should edit my profile', async () => {
const res = await sendRequest(userA.token, {
query: 'mutation($user:UserUpdateInput!) { userUpdate( user: $user) } ',
variables: {
user: { name: 'Miticå', bio: 'He never really knows what he is doing.' }
}
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.userUpdate).to.equal(true)
})
it('Should delete my account', async () => {
const userDelete = {
name: 'delete',
email: 'delete@speckle.systems',
password: 'wowwowwowwowwow'
}
userDelete.id = await createUser(userDelete)
userDelete.token = `Bearer ${await createPersonalAccessToken(
userDelete.id,
'fail token user del',
[
Scopes.Streams.Read,
Scopes.Streams.Write,
Scopes.Users.Read,
Scopes.Users.Email,
Scopes.Tokens.Write,
Scopes.Tokens.Read,
Scopes.Profile.Read,
Scopes.Profile.Email
]
)}`
const badTokenScopesBadEmail = await sendRequest(userDelete.token, {
query:
'mutation($user:UserDeleteInput!) { userDelete( userConfirmation: $user) } ',
variables: { user: { email: 'wrongEmail@email.com' } }
})
expect(badTokenScopesBadEmail.body.errors).to.exist
const badTokenScopesGoodEmail = await sendRequest(userDelete.token, {
query:
'mutation($user:UserDeleteInput!) { userDelete( userConfirmation: $user) } ',
variables: { user: { email: userDelete.email } }
})
expect(badTokenScopesGoodEmail.body.errors).to.exist
userDelete.token = `Bearer ${await createPersonalAccessToken(
userDelete.id,
'test token user del',
[
Scopes.Streams.Read,
Scopes.Streams.Write,
Scopes.Users.Read,
Scopes.Users.Email,
Scopes.Tokens.Write,
Scopes.Tokens.Read,
Scopes.Profile.Read,
Scopes.Profile.Email,
Scopes.Profile.Delete
]
)}`
const goodTokenScopesBadEmail = await sendRequest(userDelete.token, {
query:
'mutation($user:UserDeleteInput!) { userDelete( userConfirmation: $user) } ',
variables: { user: { email: 'wrongEmail@email.com' } }
})
expect(goodTokenScopesBadEmail.body.errors).to.exist
const goodTokenScopesGoodEmail = await sendRequest(userDelete.token, {
query:
'mutation($user:UserDeleteInput!) { userDelete( userConfirmation: $user) } ',
variables: { user: { email: userDelete.email } }
})
expect(goodTokenScopesGoodEmail.body.errors).to.not.exist
})
})
describe('User role change', () => {
it('User role is changed', async () => {
let queriedUserB = await sendRequest(userA.token, {
query: ` { otherUser(id:"${userB.id}") { id name role } }`
})
expect(queriedUserB.body.data.otherUser.role).to.equal(Roles.Server.User)
let query = `mutation { userRoleChange(userRoleInput: {id: "${userB.id}", role: "${Roles.Server.Admin}"})}`
await sendRequest(userA.token, { query })
queriedUserB = await sendRequest(userA.token, {
query: ` { otherUser(id:"${userB.id}") { id name role } }`
})
expect(queriedUserB.body.data.otherUser.role).to.equal(Roles.Server.Admin)
expect(queriedUserB.body.data)
query = `mutation { userRoleChange(userRoleInput: {id: "${userB.id}", role: "${Roles.Server.User}"})}`
await sendRequest(userA.token, { query })
queriedUserB = await sendRequest(userA.token, {
query: ` { otherUser(id:"${userB.id}") { id name role } }`
})
expect(queriedUserB.body.data.otherUser.role).to.equal(Roles.Server.User)
})
it('Only admins can change user role', async () => {
const query = `mutation { userRoleChange(userRoleInput: {id: "${userB.id}", role: "${Roles.Server.Admin}"})}`
const res = await sendRequest(userB.token, { query })
const queriedUserB = await sendRequest(userA.token, {
query: ` { otherUser(id:"${userB.id}") { id name role } }`
})
expect(res.body.errors).to.exist
expect(queriedUserB.body.data.otherUser.role).to.equal(Roles.Server.User)
})
})
describe('User deletion', () => {
it('Only admins can delete user', async () => {
const userDelete = {
name: 'delete',
email: 'delete@speckle.systems',
password: 'wowwowwowwowwow'
}
userDelete.id = await createUser(userDelete)
const users = await getUsers()
expect(users.map((u) => u.id)).to.contain(userDelete.id)
const query = `mutation { adminDeleteUser( userConfirmation: { email: "${userDelete.email}" } ) } `
const res = await sendRequest(userB.token, { query })
expect(res.body.errors).to.exist
expect(res.body.errors[0].extensions.code).to.equal('FORBIDDEN')
})
it('Admin can delete user', async () => {
const userDelete = {
name: 'delete',
email: 'd3l3t3@speckle.systems',
password: 'wowwowwowwowwow'
}
userDelete.id = await createUser(userDelete)
let users = await getUsers()
expect(users.map((u) => u.id)).to.contain(userDelete.id)
const query = `mutation { adminDeleteUser( userConfirmation: { email: "${userDelete.email}" } ) } `
const deleteResult = await sendRequest(userA.token, { query })
expect(deleteResult.body.data.adminDeleteUser).to.equal(true)
users = await getUsers()
expect(users.map((u) => u.id)).to.not.contain(userDelete.id)
})
it('Cannot delete the last admin', async () => {
const query = `mutation { adminDeleteUser( userConfirmation: { email: "${userA.email}" } ) } `
const res = await sendRequest(userA.token, { query })
expect(res.body.errors).to.exist
expect(res.body.errors[0].message).to.equal(
'Cannot remove the last admin role from the server'
)
})
})
describe('Streams', () => {
before(async () => {
// Moved the following from a test case, cause other tests depend on it
await Promise.all([
await sendRequest(userC.token, {
query:
'mutation { streamCreate(stream: { name: "Admin TS1 (u A) Private", description: "Hello World", isPublic:false } ) }'
}),
await sendRequest(userA.token, {
query:
'mutation { streamCreate(stream: { name: "Admin TS2 (u A)", description: "Hello Darkness", isPublic:true } ) }'
}),
await sendRequest(userB.token, {
query:
'mutation { streamCreate(stream: { name: "Admin TS3 (u B) Private", description: "Hello Pumba", isPublic:false } ) }'
}),
await sendRequest(userB.token, {
query:
'mutation { streamCreate(stream: { name: "Admin TS4 (u B)", description: "Hello Julian", isPublic:true } ) }'
}),
await sendRequest(userB.token, {
query:
'mutation { streamCreate(stream: { name: "Admin TS5 (u B)", description: "Hello King", isPublic:true } ) }'
})
])
})
it('Should create some streams', async () => {
const resS1 = await sendRequest(userA.token, {
query:
'mutation { streamCreate(stream: { name: "TS1 (u A) Private", description: "Hello World", isPublic:false } ) }'
})
expect(resS1).to.be.json
expect(resS1.body.errors).to.not.exist
expect(resS1.body.data).to.have.property('streamCreate')
expect(resS1.body.data.streamCreate).to.be.a('string')
})
it('Should update a stream', async () => {
const resS1 = await sendRequest(userA.token, {
query: `mutation { streamUpdate(stream: {id:"${ts1}" name: "TS1 (u A) Private UPDATED", description: "Hello World, Again!", isPublic:false } ) }`
})
expect(resS1).to.be.json
expect(resS1.body.errors).to.not.exist
expect(resS1.body.data).to.have.property('streamUpdate')
expect(resS1.body.data.streamUpdate).to.equal(true)
})
const publicPrivateDataset = [
{ display: 'public', isPublic: true },
{ display: 'private', isPublic: false }
]
publicPrivateDataset.forEach(({ display, isPublic }) => {
it(`Should not allow updating permissions if target user isnt a collaborator on a ${display} stream`, async () => {
const streamId = isPublic ? ts2 : ts1
const res = await sendRequest(userA.token, {
query: `mutation{ streamUpdatePermission( permissionParams: {streamId: "${streamId}", userId: "${userB.id}" role: "stream:owner"}) }`
})
expect(res).to.be.json
expect(res.body.errors).to.be.ok
expect(res.body.data.streamUpdatePermission).to.be.not.ok
expect(res.body.errors.map((e) => e.message).join('|')).to.contain(
"Cannot grant permissions to users who aren't collaborators already"
)
})
})
it('Should be able to update some permissions', async () => {
await addOrUpdateStreamCollaborator(
ts1,
userB.id,
Roles.Stream.Reviewer,
userA.id
)
const res = await sendRequest(userA.token, {
query: `mutation{ streamUpdatePermission( permissionParams: {streamId: "${ts1}", userId: "${userB.id}" role: "stream:owner"}) }`
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.streamUpdatePermission).to.equal(true)
await addOrUpdateStreamCollaborator(
ts5,
userA.id,
Roles.Stream.Reviewer,
userB.id
)
const res2 = await sendRequest(userB.token, {
query: `mutation{ streamUpdatePermission( permissionParams: {streamId: "${ts5}", userId: "${userA.id}" role: "stream:owner"}) }`
})
expect(res2).to.be.json
expect(res2.body.errors).to.not.exist
await addOrUpdateStreamCollaborator(
ts3,
userC.id,
Roles.Stream.Reviewer,
userB.id
)
const res3 = await sendRequest(userB.token, {
query: `mutation{ streamUpdatePermission( permissionParams: {streamId: "${ts3}", userId: "${userC.id}" role: "stream:owner"}) }`
})
expect(res3).to.be.json
expect(res3.body.errors).to.not.exist
})
it('Should fail to grant permissions if not owner', async () => {
const res = await sendRequest(userB.token, {
query: `mutation{ streamUpdatePermission( permissionParams: {streamId: "${ts1}", userId: "${userB.id}" role: "stream:owner"}) }`
})
expect(res).to.be.json
expect(res.body.errors).to.exist
})
it('Should fail to grant myself permissions', async () => {
const res = await sendRequest(userA.token, {
query: `mutation{ streamUpdatePermission( permissionParams: {streamId: "${ts1}", userId: "${userA.id}" role: "stream:owner"}) }`
})
expect(res).to.be.json
expect(res.body.errors).to.exist
})
it('Should revoke permissions', async () => {
await addOrUpdateStreamCollaborator(
ts3,
userC.id,
Roles.Stream.Reviewer,
userB.id
)
// first test if we can get it
const res = await sendRequest(userC.token, {
query: `query { stream(id:"${ts3}") { id name } }`
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.stream.name).to.equal('TS3 (u B) Private')
const revokeRes = await sendRequest(userB.token, {
query: `mutation { streamRevokePermission( permissionParams: {streamId: "${ts3}", userId:"${userC.id}"} ) }`
})
expect(revokeRes).to.be.json
expect(revokeRes.body.errors).to.not.exist
expect(revokeRes.body.data.streamRevokePermission).to.equal(true)
const resNotAuth = await sendRequest(userC.token, {
query: `query { stream(id:"${ts3}") { id name role } }`
})
expect(resNotAuth).to.be.json
expect(resNotAuth.body.errors).to.exist
})
it('Should fail to edit/write on a public stream if no access is provided', async () => {
// ts4 is a public stream from uesrB
const res = await sendRequest(userA.token, {
query: `mutation { streamUpdate(stream: {id:"${ts4}" name: "HACK", description: "Hello World, Again!", isPublic:false } ) }`
})
expect(res.body.errors).to.exist
})
it('Should fail editing a private stream if no access has been granted', async () => {
const res = await sendRequest(userA.token, {
query: `mutation { streamUpdate(stream: {id:"${ts3}" name: "HACK", description: "Hello World, Again!", isPublic:false } ) }`
})
expect(res.body.errors).to.exist
})
it('Should fail to delete a stream because of permissions', async () => {
// Make sure user is no longer a stream collaborator
await removeStreamCollaborator(ts1, userB.id, userB.id)
const res = await sendRequest(userB.token, {
query: `mutation { streamDelete( id:"${ts1}")}`
})
expect(res).to.be.json
expect(res.body.errors).to.exist
expect(res.body.errors[0].extensions.code).to.equal('FORBIDDEN')
})
it('Should fail to delete streams if not admin', async () => {
const res = await sendRequest(userB.token, {
query: `mutation { streamsDelete( ids:"[${ts4}]")}`
})
expect(res).to.be.json
expect(res.body.errors).to.exist
expect(res.body.errors[0].extensions.code).to.equal('FORBIDDEN')
})
it('Should delete a stream', async () => {
const res = await sendRequest(userB.token, {
query: `mutation { streamDelete( id:"${ts4}")}`
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data).to.have.property('streamDelete')
expect(res.body.data.streamDelete).to.equal(true)
})
it('Should be forbidden to query admin streams if not admin', async () => {
const res = await sendRequest(userC.token, {
query: '{ adminStreams { totalCount items { id name } } }'
})
expect(res).to.be.json
expect(res.body.errors).to.exist
expect(res.body.errors[0].extensions.code).to.equal('FORBIDDEN')
})
it('Should query admin streams', async () => {
let streamResults = await sendRequest(userA.token, {
query: '{ adminStreams { totalCount items { id name } } }'
})
expect(streamResults.body.data.adminStreams.totalCount).to.equal(10)
streamResults = await sendRequest(userA.token, {
query: '{ adminStreams(limit: 200) { totalCount items { id name } } }'
})
expect(streamResults.body.errors).to.exist
expect(streamResults.body.errors[0].extensions.code).to.equal('BAD_USER_INPUT')
streamResults = await sendRequest(userA.token, {
query: '{ adminStreams(limit: 2) { totalCount items { id name } } }'
})
expect(streamResults.body.data.adminStreams.totalCount).to.equal(10)
expect(streamResults.body.data.adminStreams.items.length).to.equal(2)
streamResults = await sendRequest(userA.token, {
query: '{ adminStreams( query: "Admin" ) { totalCount items { id name } } }'
})
expect(streamResults.body.data.adminStreams.totalCount).to.equal(5)
streamResults = await sendRequest(userA.token, {
query:
'{ adminStreams( visibility: "private" ) { totalCount items { id name isPublic } } }'
})
expect(streamResults.body.data.adminStreams.items).to.satisfy((streams) =>
streams.every((stream) => !stream.isPublic)
)
streamResults = await sendRequest(userA.token, {
query:
'{ adminStreams( visibility: "public" ) { totalCount items { id name isPublic } } }'
})
expect(streamResults.body.data.adminStreams.items).to.satisfy((streams) =>
streams.every((stream) => stream.isPublic)
)
})
it('Should delete streams', async () => {
const streamResults = await sendRequest(userA.token, {
query: '{ adminStreams( query: "Admin" ) { totalCount items { id name } } }'
})
expect(streamResults.body.data.adminStreams.totalCount).to.equal(5)
const streamIds = streamResults.body.data.adminStreams.items.map(
(stream) => stream.id
)
const res = await sendRequest(userA.token, {
query: 'mutation ( $ids: [String!] ){ streamsDelete( ids: $ids )}',
variables: { ids: streamIds }
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data).to.have.property('streamsDelete')
expect(res.body.data.streamsDelete).to.equal(true)
})
})
describe('Objects, Commits & Branches', () => {
it('Should create some objects', async () => {
const objs = []
for (let i = 0; i < 500; i++) {
if (i % 2 === 0)
objs.push({
applicationId: i,
type: 'Point',
x: i,
y: 1,
z: i * 0.42,
extra: { super: true, arr: [1, 2, 3, 4] }
})
else if (i % 3 === 0)
objs.push({
applicationId: i,
type: 'Line',
start: { x: i, y: 1, z: i * 0.42 },
end: { x: 0, y: 2, z: i * i },
extra: {
super: false,
arr: [12, 23, 34, 42, { imp: ['possible', 'this', 'sturcture', 'is'] }]
}
})
else
objs.push({
cool: ['s', 't', ['u', 'f', 'f', i], { that: true }],
iValue: i + i / 3
})
}
const res = await sendRequest(userA.token, {
query: `mutation( $objs: [JSONObject]! ) { objectCreate( objectInput: {streamId:"${ts1}", objects: $objs} ) }`,
variables: { objs }
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.objectCreate).to.have.lengthOf(objs.length)
objIds = res.body.data.objectCreate
})
it('Should create several commits', async () => {
c1.message = 'what a message for a first commit'
c1.streamId = ts1
c1.objectId = objIds[0]
c1.branchName = 'main'
let res = await sendRequest(userA.token, {
query:
'mutation( $myCommit: CommitCreateInput! ) { commitCreate( commit: $myCommit ) }',
variables: { myCommit: c1 }
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data).to.have.property('commitCreate')
expect(res.body.data.commitCreate).to.be.a('string')
c1.id = res.body.data.commitCreate
c2.message = 'what a message for a second commit'
c2.streamId = ts1
c2.objectId = objIds[1]
c2.branchName = 'main'
c2.previousCommitIds = [c1.id]
res = await sendRequest(userA.token, {
query:
'mutation( $myCommit: CommitCreateInput! ) { commitCreate( commit: $myCommit ) }',
variables: { myCommit: c2 }
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data).to.have.property('commitCreate')
expect(res.body.data.commitCreate).to.be.a('string')
c2.id = res.body.data.commitCreate
})
it('Should update a commit', async () => {
const updatePayload = {
streamId: ts1,
id: c1.id,
message: 'first commit'
}
const res = await sendRequest(userA.token, {
query:
'mutation( $myCommit: CommitUpdateInput! ) { commitUpdate( commit: $myCommit ) }',
variables: { myCommit: updatePayload }
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data).to.have.property('commitUpdate')
const res2 = await sendRequest(userB.token, {
query:
'mutation( $myCommit: CommitUpdateInput! ) { commitUpdate( commit: $myCommit ) }',
variables: { myCommit: updatePayload }
})
expect(res2).to.be.json
expect(res2.body.errors).to.exist
})
it('Should create a read receipt', async () => {
const res = await sendRequest(userA.token, {
query:
'mutation($input: CommitReceivedInput!) { commitReceive(input: $input) }',
variables: {
input: {
streamId: ts1,
commitId: c1.id,
sourceApplication: 'tests',
message: 'Irrelevant!'
}
}
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.commitReceive).to.equal(true)
const res3 = await sendRequest(null, {
query:
'mutation($input: CommitReceivedInput!) { commitReceive(input: $input) }',
variables: {
input: {
streamId: ts1,
commitId: c1.id,
sourceApplication: 'tests',
message: 'Irrelevant!'
}
}
})
expect(res3).to.be.json
expect(res3.body.errors).to.exist
expect(res3.body.errors[0].extensions.code).to.equal('FORBIDDEN')
})
it('Should delete a commit', async () => {
const payload = { streamId: ts1, id: c2.id }
const res = await sendRequest(userB.token, {
query:
'mutation( $myCommit: CommitDeleteInput! ) { commitDelete( commit: $myCommit ) }',
variables: { myCommit: payload }
})
expect(res).to.be.json
expect(res.body.errors).to.exist
const res2 = await sendRequest(userA.token, {
query:
'mutation( $myCommit: CommitDeleteInput! ) { commitDelete( commit: $myCommit ) }',
variables: { myCommit: payload }
})
expect(res2).to.be.json
expect(res2.body.errors).to.not.exist
expect(res2.body.data).to.have.property('commitDelete')
})
it('Should create several branches', async () => {
b1 = {
streamId: ts1,
name: 'dim/dev',
description: 'dimitries development branch'
}
const res1 = await sendRequest(userA.token, {
query:
'mutation( $branch:BranchCreateInput! ) { branchCreate( branch:$branch ) }',
variables: { branch: b1 }
})
expect(res1).to.be.json
expect(res1.body.errors).to.not.exist
expect(res1.body.data).to.have.property('branchCreate')
expect(res1.body.data.branchCreate).to.be.a('string')
b1.id = res1.body.data.branchCreate
b2 = {
streamId: ts1,
name: 'dim/dev/api-surgery',
description: 'another branch'
}
await addOrUpdateStreamCollaborator(
ts1,
userB.id,
Roles.Stream.Contributor,
userA.id
)
const res2 = await sendRequest(userB.token, {
query:
'mutation( $branch:BranchCreateInput! ) { branchCreate( branch:$branch ) }',
variables: { branch: b2 }
})
expect(res2.body.errors).to.not.exist
b2.id = res2.body.data.branchCreate
b3 = {
streamId: ts1,
name: 'userB/dev/api',
description: 'more branches branch'
}
const res3 = await sendRequest(userB.token, {
query:
'mutation( $branch:BranchCreateInput! ) { branchCreate( branch:$branch ) }',
variables: { branch: b3 }
})
expect(res3.body.errors).to.not.exist
b3.id = res3.body.data.branchCreate
})
it('Should update a branch', async () => {
const b1 = {
streamId: ts1,
name: 'randomupdateablebranch',
description: 'dimitries development branch'
}
const b1Res = await sendRequest(userA.token, {
query:
'mutation( $branch:BranchCreateInput! ) { branchCreate( branch:$branch ) }',
variables: { branch: b1 }
})
b1.id = b1Res.body.data.branchCreate
const payload = {
streamId: ts1,
id: b1.id,
name: 'userb/whatever/whatever'
}
const res1 = await sendRequest(userA.token, {
query:
'mutation( $branch:BranchUpdateInput! ) { branchUpdate( branch:$branch ) }',
variables: { branch: payload }
})
expect(res1).to.be.json
expect(res1.body.errors).to.not.exist
expect(res1.body.data).to.have.property('branchUpdate')
expect(res1.body.data.branchUpdate).to.equal(true)
})
it('Should delete a branch', async () => {
const b1 = {
streamId: ts1,
name: 'randomudeletablebranch',
description: 'dimitries development branch'
}
const b1Res = await sendRequest(userA.token, {
query:
'mutation( $branch:BranchCreateInput! ) { branchCreate( branch:$branch ) }',
variables: { branch: b1 }
})
b1.id = b1Res.body.data.branchCreate
// give C some access permissions
await addOrUpdateStreamCollaborator(
ts1,
userC.id,
Roles.Stream.Contributor,
userA.id
)
const payload = {
streamId: ts1,
id: b1.id
}
const badPayload = {
streamId: ts1,
id: 'APRIL FOOOLS!'
}
const res = await sendRequest(userC.token, {
query:
'mutation( $branch:BranchDeleteInput! ) { branchDelete( branch: $branch ) }',
variables: { branch: badPayload }
})
expect(res).to.be.json
expect(res.body.errors).to.exist
expect(res.body.errors[0].message).to.equal('Branch not found')
const res1 = await sendRequest(userC.token, {
query:
'mutation( $branch:BranchDeleteInput! ) { branchDelete( branch: $branch ) }',
variables: { branch: payload }
})
expect(res1).to.be.json
expect(res1.body.errors).to.exist
expect(res1.body.errors[0].message).to.equal(
'Only the branch creator or stream owners are allowed to delete branches'
)
const res2 = await sendRequest(userA.token, {
query:
'mutation( $branch:BranchDeleteInput! ) { branchDelete( branch: $branch ) }',
variables: { branch: payload }
})
expect(res2).to.be.json
expect(res2.body.errors).to.not.exist
// revoke perms for c back (dont' wanna mess up our integration-unit tests below)
await sendRequest(userA.token, {
query: `mutation{ streamRevokePermission( permissionParams: {streamId: "${ts1}", userId: "${userC.id}"} ) }`
})
})
it('Should commit to a non-main branch as well...', async () => {
const cc = {}
cc.message = 'what a message for a second commit'
cc.streamId = ts1
cc.objectId = objIds[3]
cc.branchName = 'userB/dev/api'
const res = await sendRequest(userB.token, {
query:
'mutation( $myCommit: CommitCreateInput! ) { commitCreate( commit: $myCommit ) }',
variables: { myCommit: cc }
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data).to.have.property('commitCreate')
expect(res.body.data.commitCreate).to.be.a('string')
})
it('Should *not* update a branch if given the wrong stream id', async () => {
// create stream for user C
const res = await sendRequest(userC.token, {
query:
'mutation { streamCreate(stream: { name: "TS (u C) private", description: "sup my dudes", isPublic:false } ) }'
})
ts6 = res.body.data.streamCreate
// user B creates branch on private stream
b4 = {
streamId: ts3,
name: 'izz/secret',
description: 'a private branch on a private stream'
}
const res1 = await sendRequest(userB.token, {
query:
'mutation( $branch:BranchCreateInput! ) { branchCreate( branch:$branch ) }',
variables: { branch: b4 }
})
expect(res1).to.be.json
expect(res1.body.errors).to.not.exist
expect(res1.body.data).to.have.property('branchCreate')
expect(res1.body.data.branchCreate).to.be.a('string')
b4.id = res1.body.data.branchCreate
const badPayload = {
streamId: ts6, // stream user C has access to
id: b4.id, // branch user C doesn't have access to
name: 'izz/not-so-secret'
}
const res2 = await sendRequest(userC.token, {
query:
'mutation( $branch:BranchUpdateInput! ) { branchUpdate( branch:$branch ) }',
variables: { branch: badPayload }
})
expect(res2).to.be.json
expect(res2.body.errors).to.exist
expect(res2.body.errors[0].message).to.equal(
'The branch ID and stream ID do not match, please check your inputs'
)
})
it('should *not* delete a branch if given the wrong stream id', async () => {
const badPayload = {
streamId: ts6, // stream user C has access to
id: b4.id // branch user C doesn't have access to
}
const res = await sendRequest(userC.token, {
query:
'mutation( $branch:BranchDeleteInput! ) { branchDelete( branch: $branch ) }',
variables: { branch: badPayload }
})
expect(res).to.be.json
expect(res.body.errors).to.exist
expect(res.body.errors[0].message).to.equal(
'The branch ID and stream ID do not match, please check your inputs'
)
})
})
})
describe('Queries', () => {
describe('My Profile', () => {
it('Should retrieve my profile', async () => {
const res = await sendRequest(userA.token, {
query: '{ user { id name email role apiTokens { id name } } }'
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data).to.have.property('user')
expect(res.body.data.user.name).to.equal('Miticå')
expect(res.body.data.user.email).to.equal('d.1@speckle.systems')
expect(res.body.data.user.role).to.equal(Roles.Server.Admin)
})
it('Should retrieve my streams', async () => {
// add more streams
await sendRequest(userA.token, {
query:
'mutation( $myStream: StreamCreateInput! ) { streamCreate( stream: $myStream ) }',
variables: { myStream: { name: 'o hai' } }
})
await sendRequest(userA.token, {
query:
'mutation( $myStream: StreamCreateInput! ) { streamCreate( stream: $myStream ) }',
variables: { myStream: { name: 'bai now' } }
})
await sendRequest(userA.token, {
query:
'mutation( $myStream: StreamCreateInput! ) { streamCreate( stream: $myStream ) }',
variables: { myStream: { name: 'one more for the road' } }
})
const res = await sendRequest(userA.token, {
query:
'{ user { streams( limit: 3 ) { totalCount cursor items { id name } } } }'
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.user.streams.items.length).to.equal(3)
const res2 = await sendRequest(userA.token, {
query: `{ user { streams( limit: 3, cursor: "${res.body.data.user.streams.cursor}" ) { totalCount cursor items { id name } } } }`
})
expect(res2).to.be.json
expect(res2.body.errors).to.not.exist
expect(res2.body.data.user.streams.items.length).to.equal(3)
const streams = res2.body.data.user.streams.items
const s1 = streams.find((s) => s.name === 'TS1 (u A) Private UPDATED')
expect(s1).to.exist
})
it('Should retrieve my commits (across all streams)', async () => {
for (let i = 10; i < 20; i++) {
const c1 = {
message: `what a message for commit number ${i}`,
streamId: ts1,
objectId: objIds[i],
branchName: 'main'
}
await sendRequest(userA.token, {
query:
'mutation( $myCommit: CommitCreateInput! ) { commitCreate( commit: $myCommit ) }',
variables: { myCommit: c1 }
})
}
const res = await sendRequest(userA.token, {
query:
'{ user { commits( limit: 3 ) { totalCount cursor items { id message referencedObject } } } }'
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.user.commits.totalCount).to.equal(11)
expect(res.body.data.user.commits.cursor).to.exist
expect(res.body.data.user.commits.items.length).to.equal(3)
const res2 = await sendRequest(userA.token, {
query: `{ user { commits( limit: 3, cursor: "${res.body.data.user.commits.cursor}") { totalCount cursor items { id message referencedObject } } } }`
})
expect(res2).to.be.json
expect(res2.body.errors).to.not.exist
expect(res2.body.data.user.commits.totalCount).to.equal(11)
expect(res2.body.data.user.commits.items.length).to.equal(3)
})
})
describe('Different Users` Profile', () => {
/**
* TODO: These user() queries should be swapped to otherUser() afterwards
*/
it('Should retrieve a different profile profile', async () => {
const res = await sendRequest(userA.token, {
query: ` { user(id:"${userB.id}") { id name email } }`
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data).to.have.property('user')
expect(res.body.data.user.name).to.equal('d2')
expect(res.body.data.user.email).to.equal('d.2@speckle.systems')
})
it('Should not retrieve a profile if no auth', async () => {
const res = await sendRequest(null, { query: '{ user { id name email } }' })
expect(res).to.be.json
expect(res.body.data).to.have.property('user')
expect(res.body.data.user).to.be.null
expect(res.body.errors).to.not.exist
})
it('Should not retrieve user email field if out of scope', async () => {
// token1 has only users:read scope
const res = await sendRequest(token1, {
query: ` { user(id:"${userB.id}") { id name email } }`
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.user.email).to.be.null
})
it('Should only retrieve public streams from a different user profile ', async () => {
const res = await sendRequest(token1, {
query: `query { user( id:"${userB.id}" ) { streams { totalCount items { id name isPublic } } } }`
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.user.streams.totalCount).to.equal(1)
})
it('Should search for some users', async () => {
for (let i = 0; i < 10; i++) {
// create 10 users: 3 bakers and 7 millers
await createUser({
name: `Master ${i <= 2 ? 'Baker' : 'Miller'} Matteo The ${i}${
i === 1 ? 'st' : i === 2 ? 'nd' : i === 3 ? 'rd' : 'th'
} of His Name`,
email: `matteo_${i}@tomato.com`,
password: `${
i % 2 === 0 ? 'BakerBakerBakerBaker' : 'TomatoTomatoTomatoTomato'
}`
})
}
let query = `
query search {
userSearch( query: "miller" ) {
cursor
items {
id
name
}
}
}
`
let res = await sendRequest(userB.token, { query })
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.userSearch.items.length).to.equal(7)
query = `
query search {
userSearch( query: "baker" ) {
cursor
items {
id
name
}
}
}
`
res = await sendRequest(userB.token, { query })
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.userSearch.items.length).to.equal(3)
// by email
query =
'query { userSearch( query: "matteo_2@tomato.com" ) { cursor items { id name } } } '
res = await sendRequest(userB.token, { query })
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.userSearch.items.length).to.equal(1)
})
it('Should not search for some users if bad request', async () => {
const queryLim =
'query { userSearch( query: "mi" ) { cursor items { id name } } } '
let res = await sendRequest(userB.token, { query: queryLim })
expect(res).to.be.json
expect(res.body.errors).to.exist
const queryPagination =
'query { userSearch( query: "matteo", limit: 200 ) { cursor items { id name } } } '
res = await sendRequest(userB.token, { query: queryPagination })
expect(res).to.be.json
expect(res.body.errors).to.exist
})
})
describe('Streams', () => {
it('Should retrieve a stream', async () => {
const res = await sendRequest(userA.token, {
query: `
query {
stream(id:"${ts1}") {
id
name
createdAt
updatedAt
collaborators {
id
name
role
}
}
}`
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
const stream = res.body.data.stream
expect(stream.name).to.equal('TS1 (u A) Private UPDATED')
expect(stream.collaborators).to.have.lengthOf(2)
expect(stream.collaborators[0].role).to.equal(Roles.Stream.Contributor)
expect(stream.collaborators[1].role).to.equal(Roles.Stream.Owner)
})
it('Should retrieve a public stream even if not authenticated', async () => {
const query = `query { stream( id: "${ts2}" ) { name createdAt } }`
const res = await sendRequest(null, { query })
expect(res).to.be.json
expect(res.body.errors).to.not.exist
})
it('should retrieve all stream branches', async () => {
const query = `
query{
stream(id: "${ts1}"){
branches {
totalCount
cursor
items {
id
name
author {
id
name
}
}
}
}
}
`
const res = await sendRequest(userA.token, { query })
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.stream.branches.items).to.be.ok
expect(res.body.data.stream.branches.totalCount).to.be.ok
expect(res.body.data.stream.branches.cursor).to.exist
const firstBranchName = res.body.data.stream.branches.items[0].name
const res1 = await sendRequest(userA.token, {
query: `query { stream(id:"${ts1}") { branch( name: "${firstBranchName}" ) { name description } } } `
})
expect(res1).to.be.json
expect(res1.body.errors).to.not.exist
expect(res1.body.data.stream.branch.name).to.equal(firstBranchName)
})
it("it should retrieve a stream's default 'main' branch if no branch name is specified", async () => {
const res = await sendRequest(userA.token, {
query: `query { stream(id:"${ts1}") { branch { name description } } } `
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.stream.branch.name).to.equal('main')
})
it('should retrieve a branch`s commits', async () => {
const query = `
query {
stream( id: "${ts1}" ) {
branch( name: "main" ) {
id
name
commits( limit: 5 ) {
totalCount
cursor
items {
id
message
createdAt
referencedObject
authorId
}
}
}
}
}
`
const res = await sendRequest(userA.token, { query })
expect(res.body.data.stream.branch.commits.items.length).to.equal(5)
expect(res.body.data.stream.branch.commits.items[0]).to.have.property('id')
expect(res.body.data.stream.branch.commits.items[0]).to.have.property('message')
expect(res.body.data.stream.branch.commits.items[0]).to.have.property(
'createdAt'
)
const query2 = `
query {
stream( id: "${ts1}" ) {
branch( name: "main" ) {
id
name
commits( limit: 3, cursor: "${res.body.data.stream.branch.commits.cursor}" ) {
totalCount
cursor
items {
id
message
createdAt
referencedObject
authorId
authorName
}
}
}
}
}`
const res2 = await sendRequest(userA.token, { query: query2 })
// console.log( res2.body.errors )
// console.log( res2.body.data.stream.branch.commits )
expect(res2.body.data.stream.branch.commits.items.length).to.equal(3)
expect(res2.body.data.stream.branch.commits.items[0]).to.have.property('id')
expect(res2.body.data.stream.branch.commits.items[0]).to.have.property(
'message'
)
expect(res2.body.data.stream.branch.commits.items[0]).to.have.property(
'createdAt'
)
})
let commitList
it('should retrieve all stream commits', async () => {
const query = `
query {
stream( id: "${ts1}" ) {
commits( limit: 10 ) {
totalCount
cursor
items {
id
message
authorId
authorName
}
}
}
}
`
const res = await sendRequest(userA.token, { query })
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.stream.commits.items.length).to.equal(10)
expect(res.body.data.stream.commits.totalCount).to.equal(12)
commitList = res.body.data.stream.commits.items
const query2 = `
query {
stream( id: "${ts1}" ) {
commits( limit: 10, cursor: "${res.body.data.stream.commits.cursor}" ) {
totalCount
cursor
items {
id
message
authorId
authorName
}
}
}
}
`
const res2 = await sendRequest(userA.token, { query: query2 })
expect(res2).to.be.json
expect(res2.body.errors).to.not.exist
expect(res2.body.data.stream.commits.items.length).to.equal(2)
})
it('should retrieve a stream commit', async () => {
const res = await sendRequest(userA.token, {
query: `query { stream( id:"${ts1}" ) { commit( id: "${commitList[0].id}" ) { id message referencedObject } } }`
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.stream.commit.message).to.equal(
'what a message for commit number 19'
) // should be the last created one
})
it('should retrieve the latest stream commit if no id is specified', async () => {
const res = await sendRequest(userA.token, {
query: `query { stream( id:"${ts1}" ) { commit { id message referencedObject } } }`
})
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.stream.commit.message).to.equal(
'what a message for commit number 19'
) // should be the last created one
})
})
describe('Objects', () => {
let myCommit
let myObjs
before(async () => {
const { commit, objs } = generateManyObjects(100, 'noise__')
myCommit = commit
myObjs = objs
})
it('should save many objects', async () => {
const everything = [myCommit, ...myObjs]
const res = await sendRequest(userA.token, {
query: `mutation($objs:[JSONObject]!) { objectCreate(objectInput: {streamId:"${ts1}", objects: $objs}) }`,
variables: { objs: everything }
})
const objIds = res.body.data.objectCreate
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(objIds.length).to.equal(101) // +1 for the actual "commit" object
})
it("should get an object's subojects objects", async () => {
const first = await sendRequest(userA.token, {
query: `
query {
stream( id:"${ts1}" ) {
id
name
object( id:"${myCommit.id}" ) {
createdAt
children( limit: 2 ) {
totalCount
cursor
objects {
id
}
}
}
}
}
`
})
expect(first).to.be.json
expect(first.body.errors).to.not.exist
expect(first.body.data.stream).to.be.an('object')
expect(first.body.data.stream.object).to.be.an('object')
expect(first.body.data.stream.object.children.objects.length).to.equal(2)
const second = await sendRequest(userA.token, {
query: `
query {
stream(id:"${ts1}") {
id
name
object( id:"${myCommit.id}" ) {
createdAt
children( limit: 20, cursor: "${first.body.data.stream.object.children.cursor}", select: ["sortValueA", "nest.arr[2]"] ) {
totalCount
objects {
id
data
}
}
}
}
}
`
})
expect(second).to.be.json
expect(second.body.errors).to.not.exist
expect(second.body.data.stream).to.be.an('object')
expect(second.body.data.stream.object).to.be.an('object')
expect(second.body.data.stream.object.children.objects.length).to.equal(20)
expect(
second.body.data.stream.object.children.objects[0].data.sortValueA
).to.equal(52) // when sorting by id, it's always 52
expect(
second.body.data.stream.object.children.objects[0].data.nest.arr[2]
).to.equal(52) // when sorting by id, it's always 52
})
it("should query an object's subojects", async () => {
const first = await sendRequest(userA.token, {
query: `
query( $query: [JSONObject!], $orderBy: JSONObject ) {
stream(id:"${ts1}") {
id
name
object( id:"${myCommit.id}" ) {
createdAt
children( limit: 20, select:[ "sortValueA" ], query: $query, orderBy: $orderBy ) {
totalCount
cursor
objects {
id
data
}
}
}
}
}
`,
variables: {
query: [{ field: 'sortValueA', operator: '>=', value: 42 }],
orderBy: { field: 'sortValueA' }
}
})
expect(first).to.be.json
expect(first.body.errors).to.not.exist
expect(first.body.data.stream).to.be.an('object')
expect(first.body.data.stream.object).to.be.an('object')
expect(first.body.data.stream.object.children.objects.length).to.equal(20)
expect(
first.body.data.stream.object.children.objects[0].data.sortValueA
).to.equal(42)
expect(
first.body.data.stream.object.children.objects[1].data.sortValueA
).to.equal(43)
})
})
})
describe('Generic / Server Info', () => {
it('Should eval string for password strength', async () => {
const query = `query {
userPwdStrength(pwd: "garbage") {
score
feedback {
warning
suggestions
}
}
} `
const res = await sendRequest(null, { query })
expect(res).to.be.json
expect(res.body.errors).to.not.exist
})
it('Should return a valid server information object', async () => {
const q = `
query{
serverInfo{
name
adminContact
termsOfService
description
version
roles{
name
description
resourceTarget
}
scopes{
name
description
}
}
}`
const res = await sendRequest(null, { query: q })
expect(res).to.be.json
expect(res.body.errors).to.not.exist
expect(res.body.data.serverInfo).to.be.an('object')
const si = res.body.data.serverInfo
expect(si.name).to.be.a('string')
expect(si.adminContact).to.be.a('string')
expect(si.termsOfService).to.be.a('string')
expect(si.description).to.be.a('string')
expect(si.roles).to.be.a('array')
expect(si.scopes).to.be.a('array')
})
it('Should update the server info object', async () => {
const query =
'mutation updateSInfo($info: ServerInfoUpdateInput!) { serverInfoUpdate( info: $info ) } '
const variables = {
info: { name: 'Super Duper Test Server Yo!', company: 'Super Systems' }
}
const res = await sendRequest(userA.token, { query, variables })
expect(res).to.be.json
expect(res.body.errors).to.not.exist
})
it('Should NOT update the server info object if user is not an admin', async () => {
const query =
'mutation updateSInfo( $info: ServerInfoUpdateInput! ) { serverInfoUpdate( info: $info ) } '
const variables = {
info: { name: 'Super Duper Test Server Yo!', company: 'Super Systems' }
}
const res = await sendRequest(userB.token, { query, variables })
expect(res).to.be.json
expect(res.body.errors).to.exist
})
})
describe('Archived role access validation', () => {
const archivedUser = {
name: 'Mark von Archival',
email: 'archi@speckle.systems',
password: 'i"ll be back, just wait'
}
let streamId
before(async () => {
archivedUser.id = await createUser(archivedUser)
archivedUser.token = `Bearer ${await createPersonalAccessToken(
archivedUser.id,
'this will be archived',
[
Scopes.Streams.Read,
Scopes.Streams.Write,
Scopes.Users.Read,
Scopes.Users.Email,
Scopes.Tokens.Write,
Scopes.Tokens.Read,
Scopes.Profile.Read,
Scopes.Profile.Email,
Scopes.Apps.Read,
Scopes.Apps.Write,
Scopes.Users.Invite
]
)}`
await changeUserRole({ userId: archivedUser.id, role: Roles.Server.ArchivedUser })
})
it('Should be able to read public streams', async () => {
const streamRes = await sendRequest(userA.token, {
query:
'mutation { streamCreate( stream: { name: "Share this with poor Mark", description: "💩", isPublic:true } ) }'
})
await addOrUpdateStreamCollaborator(
streamRes.body.data.streamCreate,
archivedUser.id,
Roles.Stream.Contributor,
userA.id
)
const res = await sendRequest(archivedUser.token, {
query: `query { stream(id:"${streamRes.body.data.streamCreate}") { id name } }`
})
expect(res.body.errors).to.not.exist
expect(res.body.data.stream.id).to.equal(streamRes.body.data.streamCreate)
})
it('Should be forbidden to create token', async () => {
const query =
'mutation( $tokenInput:ApiTokenCreateInput! ) { apiTokenCreate ( token: $tokenInput ) }'
const res = await sendRequest(archivedUser.token, {
query,
variables: {
tokenInput: {
scopes: [Scopes.Streams.Read],
name: 'thisWillNotBeCreated',
lifespan: 1000000
}
}
})
// WHY NOT 401 ???
// expect( res ).to.have.status( 401 )
expect(res.body.errors).to.exist
expect(res.body.errors[0].message).to.equal(
'You do not have the required server role'
)
})
it('Should be forbidden to interact (read, write, delete) private streams it had access to', async () => {
const streamRes = await sendRequest(userA.token, {
query:
'mutation { streamCreate( stream: { name: "Share this with poor Mark", description: "💩", isPublic:false } ) }'
})
streamId = streamRes.body.data.streamCreate
await addOrUpdateStreamCollaborator(
streamId,
archivedUser.id,
Roles.Stream.Contributor,
userA.id
)
let res = await sendRequest(archivedUser.token, {
query: `query { stream(id:"${streamId}") { id name } }`
})
expect(res.body.errors).to.exist
expect(res.body.errors[0].message).to.equal(
'You do not have the required server role'
)
res = await sendRequest(archivedUser.token, {
query:
'{ user { streams( limit: 30 ) { totalCount cursor items { id name } } } }'
})
expect(res.body.errors).to.exist
expect(res.body.errors[0].message).to.equal(
'You do not have the required server role'
)
res = await sendRequest(archivedUser.token, {
query: `mutation { streamDelete( id:"${streamId}")}`
})
expect(res.body.errors).to.exist
expect(res.body.errors[0].message).to.equal(
'You do not have the required server role'
)
res = await sendRequest(archivedUser.token, {
query: `mutation { streamUpdate(stream: {id:"${streamId}" name: "HACK", description: "Hello World, Again!", isPublic:false } ) }`
})
expect(res.body.errors).to.exist
expect(res.body.errors[0].message).to.equal(
'You do not have the required server role'
)
})
it('Should be forbidden to create streams, both public and private', async () => {
const query =
'mutation ( $streamInput: StreamCreateInput!) { streamCreate(stream: $streamInput ) }'
let res = await sendRequest(archivedUser.token, {
query,
variables: {
streamInput: {
name: 'Trying to create stream',
description: '💩',
isPublic: false
}
}
})
expect(res.body.errors).to.exist
expect(res.body.errors[0].message).to.equal(
'You do not have the required server role'
)
res = await sendRequest(archivedUser.token, {
query,
variables: {
streamInput: {
name: 'Trying to create stream',
description: '💩',
isPublic: true
}
}
})
expect(res.body.errors).to.exist
expect(res.body.errors[0].message).to.equal(
'You do not have the required server role'
)
})
it('Should be forbidden to add apps', async () => {
const query =
'mutation createApp($myApp:AppCreateInput!) { appCreate( app: $myApp ) } '
const variables = {
myApp: {
name: 'Test App',
public: true,
description: 'Test App Description',
scopes: [Scopes.Streams.Read],
redirectUrl: 'lol://what'
}
}
const res = await sendRequest(archivedUser.token, { query, variables })
expect(res.body.errors).to.exist
expect(res.body.errors[0].message).to.equal(
'You do not have the required server role'
)
})
it('Should be forbidden to send email invites', async () => {
const res = await sendRequest(archivedUser.token, {
query:
'mutation inviteToServer($input: ServerInviteCreateInput!) { serverInviteCreate( input: $input ) }',
variables: { input: { email: 'cabbages@speckle.systems', message: 'wow!' } }
})
expect(res.body.errors).to.exist
expect(res.body.errors[0].message).to.equal(
'You do not have the required server role'
)
})
it('Should be forbidden to create object', async () => {
const objects = generateManyObjects(10)
const res = await sendRequest(archivedUser.token, {
query: `mutation( $objs: [JSONObject]! ) { objectCreate( objectInput: {streamId:"${ts1}", objects: $objs} ) }`,
variables: { objs: objects.objs }
})
expect(res.body.errors).to.exist
expect(res.body.errors[0].message).to.equal(
'You do not have the required server role'
)
})
it('Should be forbidden to create commit', async () => {
const commit = {
message: 'what a message for a first commit',
streamId,
objectId: 'justARandomHash',
branchName: 'main'
}
const res = await sendRequest(archivedUser.token, {
query:
'mutation( $myCommit: CommitCreateInput! ) { commitCreate( commit: $myCommit ) }',
variables: { myCommit: commit }
})
expect(res.body.errors).to.exist
expect(res.body.errors[0].message).to.equal(
'You do not have the required server role'
)
})
it('Should be forbidden to upload via rest API', async () => {
const objects = generateManyObjects(2)
const res = await request(app)
.post(`/objects/${streamId}`)
.set('Authorization', archivedUser.token)
.set('Content-type', 'multipart/form-data')
.attach('batch1', Buffer.from(JSON.stringify(objects.objs), 'utf8'))
expect(res).to.have.status(401)
})
it('Should be forbidden to download from private stream it had access to via rest API', async () => {
// even if the object doesn't exist, so im not creating it...
const res = await request(app)
.get('/objects/thisIs/bogus')
.set('Authorization', archivedUser.token)
expect(res).to.have.status(401)
})
it('Should be able to download from public stream via rest API', async () => {
const streamRes = await sendRequest(userA.token, {
query:
'mutation { streamCreate( stream: { name: "Mark will read this", description: "🥔", isPublic:true } ) }'
})
await addOrUpdateStreamCollaborator(
streamRes.body.data.streamCreate,
archivedUser.id,
Roles.Stream.Contributor,
userA.id
)
const objects = generateManyObjects(2)
let res = await request(app)
.post(`/objects/${streamRes.body.data.streamCreate}`)
.set('Authorization', userA.token)
.set('Content-type', 'multipart/form-data')
.attach('batch1', Buffer.from(JSON.stringify(objects.objs), 'utf8'))
expect(res).to.have.status(201)
res = await request(app)
.get(`/objects/${streamRes.body.data.streamCreate}/${objects.objs[0].id}`)
.set('Authorization', archivedUser.token)
expect(res).to.have.status(200)
expect(res.body[0].id).to.equal(objects.objs[0].id)
})
})
})