From c1924ba54926268e54b031a6b723ca16559e60bc Mon Sep 17 00:00:00 2001 From: Dimitrie Stefanescu Date: Sat, 25 Apr 2020 20:56:16 +0100 Subject: [PATCH] test(graphql): adding more tests --- modules/core/graph/schemas/objects.graphql | 14 +- modules/core/index.js | 24 +++ modules/core/streams/services.js | 2 + modules/core/tests/graph.spec.js | 206 ++++++++++++++++++--- 4 files changed, 220 insertions(+), 26 deletions(-) diff --git a/modules/core/graph/schemas/objects.graphql b/modules/core/graph/schemas/objects.graphql index 80eb27df4..4b258dcaa 100644 --- a/modules/core/graph/schemas/objects.graphql +++ b/modules/core/graph/schemas/objects.graphql @@ -120,9 +120,17 @@ extend type Mutation { Deletes a branch. """ branchDelete( streamId: String!, branchId: String! ):Boolean! - + """ + Creates a tag. + """ tagCreate(streamId: String!, tag: TagCreateInput ): String! - tagUpdate(streamId: String!, tag: TagUpdateInput ): String + """ + Updates a tag. + """ + tagUpdate(streamId: String!, tag: TagUpdateInput ): Boolean! + """ + Deletes a tag. + """ tagDelete( streamId: String!, tagId: String! ): String } @@ -158,7 +166,7 @@ input CommitCreateInput { """ streamId: String! """ - The objects you want to create. + The commit you want to create. """ commit: JSONObject! } diff --git a/modules/core/index.js b/modules/core/index.js index f14b435b9..39fc34011 100644 --- a/modules/core/index.js +++ b/modules/core/index.js @@ -3,6 +3,30 @@ let debug = require( 'debug' )( 'speckle:modules' ) exports.preflight = ( options ) => { debug( 'Preflight core modules' ) + + // TODO: register scopes exposed by this module + // Base scope schema: + // type Scope { + // name: String! ie streams:read + // description: String! (human readable explanation) + // } + + // Example scopes: + // streams:read, streams:write, users:email, tokens:read, tokens:create + + // TODO: register roles exposed by this module. + // Base role schema: + // type Role { + // name: String! (simple name) + // description: String! (describe its behaviour) + // resourceTarget: String! (describe which resource it should apply to) + // aclTableName: String! (where are they stored) + // } + + // Example roles (in use) for streams: + // OWNER + // CONTRIBUTOR + // REVIEWER } exports.init = ( app, options ) => { diff --git a/modules/core/streams/services.js b/modules/core/streams/services.js index b9a6c994c..6c3a52aec 100644 --- a/modules/core/streams/services.js +++ b/modules/core/streams/services.js @@ -48,6 +48,7 @@ module.exports = { let query = Acl( ).insert( { userId: userId, resourceId: streamId, role: role } ).toString( ) + ` on conflict on constraint stream_acl_pkey do update set role=excluded.role` await knex.raw( query ) + return true }, async revokePermissionsStream( streamId, userId ) { @@ -60,6 +61,7 @@ module.exports = { if ( delCount === 0 ) throw new Error( 'Could not revoke permissions for user. Is he an owner?' ) + return true }, async deleteStream( streamId ) { diff --git a/modules/core/tests/graph.spec.js b/modules/core/tests/graph.spec.js index f2d4a2dba..83b11b798 100644 --- a/modules/core/tests/graph.spec.js +++ b/modules/core/tests/graph.spec.js @@ -12,11 +12,12 @@ const knex = require( `${root}/db/knex` ) const { createUser, createToken } = require( '../users/services' ) +let addr + describe( 'GraphQL API Core', ( ) => { let userA = { name: 'd1', username: 'd1', email: 'd.1@speckle.systems', password: 'wow' } let userB = { name: 'd2', username: 'd2', email: 'd.2@speckle.systems', password: 'wow' } let testServer - let addr before( async ( ) => { await knex.migrate.latest( ) @@ -34,36 +35,195 @@ describe( 'GraphQL API Core', ( ) => { after( async ( ) => { await knex.migrate.rollback( ) - testServer.close() + testServer.close( ) } ) + // the four stream ids + let ts1 + let ts2 + let ts3 + let ts4 + + let objIds + describe( 'Mutations', ( ) => { - it( 'Should create a stream', async ( ) => { - assert.fail('todo') + + it( 'Should create some api tokens', async ( ) => { + assert.fail( 'todo' ) } ) + + it( 'Should revoke an api token', async ( ) => { + assert.fail( 'todo' ) + } ) + + it( 'Should fail to revoke an api token', async ( ) => { + assert.fail( 'todo' ) + } ) + + 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 ).to.have.status( 200 ) + expect( resS1.body.data ).to.have.property( 'streamCreate' ) + expect( resS1.body.data.streamCreate ).to.be.a( 'string' ) + 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 + } ) + + it( 'Should grant some permissions', async ( ) => { + const res = await sendRequest( userA.token, { query: `mutation{ streamGrantPermission( streamId: "${ts1}", userId: "${userB.id}" role: WRITE) }` } ) + + expect( res ).to.be.json + expect( res ).to.have.status( 200 ) + expect( res.body.data.streamGrantPermission ).to.equal( true ) + } ) + + it( 'Should update permissions', async ( ) => { + const res = await sendRequest( userA.token, { query: `mutation{ streamGrantPermission( streamId: "${ts1}", userId: "${userB.id}" role: READ) }` } ) + + expect( res ).to.be.json + expect( res ).to.have.status( 200 ) + expect( res.body.data.streamGrantPermission ).to.equal( true ) + } ) + + it( 'Should create some objects', async ( ) => { + let 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(streamId:"${ts1}", objects: $objs) }`, variables: { objs: objs } } ) + + expect( res ).to.be.json + expect( res ).to.have.status( 200 ) + expect( res.body.data.objectCreate ).to.have.lengthOf( objs.length ) + + objIds = res.body.data.objectCreate + + } ) + + let c1 = { description: 'test first commit' } + let c2 = { description: 'test second commit' } + + it( 'Should create several commits', async ( ) => { + let res = await sendRequest( userA.token, { query: `mutation($commit:JSONObject!) { commitCreate(streamId:"${ts1}", commit:$commit) }`, variables: { commit: c1 } } ) + + expect( res ).to.be.json + expect( res ).to.have.status( 200 ) + expect( res.body.data ).to.have.property( 'commitCreate' ) + expect( res.body.data.commitCreate ).to.be.a( 'string' ) + c1.id = res.body.data.commitCreate + + res = await sendRequest( userA.token, { query: `mutation($commit:JSONObject!) { commitCreate(streamId:"${ts1}", commit:$commit) }`, variables: { commit: c2 } } ) + c2.id = res.body.data.commitCreate + } ) + + let tag1 = { name: 'v.10.0.0', description: 'test tag' } + let tag2 = { name: 'v.20.0.0' } + let tag3 = { name: 'v.21.0.1-alpha' } + + it( 'Should create two tags', async ( ) => { + tag1.commitId = c1.id + tag2.commitId = c2.id + + let res = await sendRequest( userA.token, { query: ` + mutation($tag: TagCreateInput){tagCreate(streamId:"${ts1}", tag: $tag) }`, variables: { tag: tag1 } } ) + + expect( res ).to.be.json + expect( res ).to.have.status( 200 ) + expect( res.body.data ).to.have.property( 'tagCreate' ) + tag1.id = res.body.data.tagCreate + + // create a second tag + res = await sendRequest( userA.token, { query: ` + mutation($tag: TagCreateInput){tagCreate(streamId:"${ts1}", tag: $tag)}`, variables: { tag: tag2 } } ) + expect( res ).to.be.json + expect( res ).to.have.status( 200 ) + expect( res.body.data ).to.have.property( 'tagCreate' ) + tag2.id = res.body.data.tagCreate + + tag3.commitId = c2.id + res = await sendRequest( userA.token, { query: ` + mutation($tag: TagCreateInput){tagCreate(streamId:"${ts1}", tag: $tag)}`, variables: { tag: tag3 } } ) + + } ) + + it( 'Should update a tag', async ( ) => { + const res = await sendRequest( userA.token, { query: ` + mutation($tag: TagUpdateInput){tagUpdate(streamId:"${ts1}", tag: $tag)}`, variables: { tag: { id: tag2.id, description: 'Cool description!' } } } ) + expect( res ).to.be.json + expect( res ).to.have.status( 200 ) + expect( res.body.data ).to.have.property( 'tagUpdate' ) + expect( res.body.data.tagUpdate ).to.equal( true ) + } ) + + it( 'Should create several branches', async ( ) => { + assert.fail( 'todo' ) + } ) + + it( 'Should update a branch', async ( ) => { + assert.fail( 'todo' ) + } ) + + it( 'Should delete a branch', async ( ) => { + assert.fail( 'todo' ) + } ) + + it( 'Authorization check: should fail on private stream with no access', async ( ) => { + assert.fail( 'todo' ) + } ) + } ) describe( 'Queries', ( ) => { - it( 'Should retrieve my profile', async ( ) => { - const res = await chai.request( addr ).post( '/graphql' ).set( 'Authorization', userA.token ).send( { - query: ` - { - user { - id - name - email - } - } + describe( 'Users', ( ) => { + it( 'Should retrieve my profile', async ( ) => { + const res = await sendRequest( userA.token, { + query: `{ user { id name email } }` + } ) - ` + expect( res ).to.be.json + expect( res ).to.have.status( 200 ) + expect( res.body.data ).to.have.property( 'user' ) + expect( res.body.data.user.name ).to.equal( 'd1' ) + expect( res.body.data.user.email ).to.equal( 'd.1@speckle.systems' ) } ) - console.log( res.body ) - expect( res ).to.be.json - expect( res ).to.have.status( 200 ) - expect( res.body.data ).to.have.property( 'user' ) - expect( res.body.data.user.name ).to.equal( 'd1' ) - expect( res.body.data.user.email ).to.equal( 'd.1@speckle.systems' ) - // console.log( res ) + + 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 ).to.have.status( 200 ) + 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' ) + } ) + } ) + + describe( 'Streams', ( ) => { + } ) } ) -} ) \ No newline at end of file +} ) + +/** + * Sends a graphql reuqest. Convenience wrapper. + * @param {string} auth the users's token + * @param {string} obj the query/mutation to send + * @return {Promise} the awaitable request + */ +function sendRequest( auth, obj ) { + return chai.request( addr ).post( '/graphql' ).set( 'Authorization', auth ).send( obj ) +} \ No newline at end of file