From 6cbfd8d4ed2aefa9da09b062fc88129bb3342f96 Mon Sep 17 00:00:00 2001 From: Dimitrie Stefanescu Date: Thu, 10 Jun 2021 14:00:12 +0100 Subject: [PATCH] feat(server): admin backend: adds stat services & tests --- .../server/modules/core/tests/objects.spec.js | 12 +- .../server/modules/stats/services/index.js | 80 ++++++++- .../server/modules/stats/tests/stats.spec.js | 152 ++++++++++++++++++ 3 files changed, 230 insertions(+), 14 deletions(-) create mode 100644 packages/server/modules/stats/tests/stats.spec.js diff --git a/packages/server/modules/core/tests/objects.spec.js b/packages/server/modules/core/tests/objects.spec.js index a83e3d556..aaf84967a 100644 --- a/packages/server/modules/core/tests/objects.spec.js +++ b/packages/server/modules/core/tests/objects.spec.js @@ -480,8 +480,8 @@ describe( 'Objects @core-objects', ( ) => { const crypto = require( 'crypto' ) -function createManyObjects( shitTon, noise ) { - shitTon = shitTon || 10000 +function createManyObjects( num, noise ) { + num = num || 10000 noise = noise || Math.random( ) * 100 let objs = [ ] @@ -490,7 +490,7 @@ function createManyObjects( shitTon, noise ) { objs.push( base ) let k = 0 - for ( let i = 0; i < shitTon; i++ ) { + for ( let i = 0; i < num; i++ ) { let baby = { name: `mr. ${i}`, nest: { duck: i % 2 === 0, mallard: 'falsey', arr: [ i + 42, i, i ] }, @@ -504,7 +504,7 @@ function createManyObjects( shitTon, noise ) { } if ( i % 3 === 0 ) k++ - getAFuckingId( baby ) + getAnId( baby ) base.__closure[ baby.id ] = 1 if ( i > 1000 ) @@ -513,10 +513,10 @@ function createManyObjects( shitTon, noise ) { objs.push( baby ) } - getAFuckingId( base ) + getAnId( base ) return objs } -function getAFuckingId( obj ) { +function getAnId( obj ) { obj.id = obj.id || crypto.createHash( 'md5' ).update( JSON.stringify( obj ) ).digest( 'hex' ) } diff --git a/packages/server/modules/stats/services/index.js b/packages/server/modules/stats/services/index.js index 4fc463945..54b9b58c0 100644 --- a/packages/server/modules/stats/services/index.js +++ b/packages/server/modules/stats/services/index.js @@ -1,29 +1,93 @@ 'use strict' +const appRoot = require( 'app-root-path' ) +const knex = require( `${appRoot}/db/knex` ) module.exports = { async getTotalStreamCount() { - // TODO + const query = 'SELECT COUNT(*) FROM streams' + const result = await knex.raw( query ) + return parseInt( result.rows[0].count ) }, async getTotalCommitCount() { - // TODO + const query = 'SELECT COUNT(*) FROM commits' + const result = await knex.raw( query ) + return parseInt( result.rows[0].count ) }, async getTotalObjectCount() { - // TODO + const query = 'SELECT COUNT(*) FROM objects' + const result = await knex.raw( query ) + return parseInt( result.rows[0].count ) }, async getTotalUserCount() { - // TODO + const query = 'SELECT COUNT(*) FROM users' + const result = await knex.raw( query ) + return parseInt( result.rows[0].count ) }, - async getStreamHistory() {}, + async getStreamHistory() { + const query = ` + SELECT + DATE_TRUNC('month', streams. "createdAt") AS created_month, + COUNT(id) AS count + FROM + streams + GROUP BY + DATE_TRUNC('month', streams. "createdAt") + ` - async getCommitHistory() {}, + const result = await knex.raw( query ) + result.rows.forEach( row => row.count = parseInt( row.count ) ) + return result.rows + }, - async getObjectHistory() {}, + async getCommitHistory() { + const query = ` + SELECT + DATE_TRUNC('month', commits. "createdAt") AS created_month, + COUNT(id) AS count + FROM + commits + GROUP BY + DATE_TRUNC('month', commits. "createdAt") + ` + const result = await knex.raw( query ) + result.rows.forEach( row => row.count = parseInt( row.count ) ) + return result.rows + }, - async getUserHistory() {}, + async getObjectHistory() { + const query = ` + SELECT + DATE_TRUNC('month', objects. "createdAt") AS created_month, + COUNT(id) AS count + FROM + objects + GROUP BY + DATE_TRUNC('month', objects. "createdAt") + ` + const result = await knex.raw( query ) + result.rows.forEach( row => row.count = parseInt( row.count ) ) + return result.rows + + }, + + async getUserHistory() { + const query = ` + SELECT + DATE_TRUNC('month', users. "createdAt") AS created_month, + COUNT(id) AS count + FROM + users + GROUP BY + DATE_TRUNC('month', users. "createdAt") + ` + const result = await knex.raw( query ) + result.rows.forEach( row => row.count = parseInt( row.count ) ) + return result.rows + }, } diff --git a/packages/server/modules/stats/tests/stats.spec.js b/packages/server/modules/stats/tests/stats.spec.js new file mode 100644 index 000000000..ee0108031 --- /dev/null +++ b/packages/server/modules/stats/tests/stats.spec.js @@ -0,0 +1,152 @@ +/* istanbul ignore file */ +const chai = require( 'chai' ) +const chaiHttp = require( 'chai-http' ) +const assert = require( 'assert' ) + +const appRoot = require( 'app-root-path' ) +const { init } = require( `${appRoot}/app` ) +const knex = require( `${appRoot}/db/knex` ) + +const expect = chai.expect +chai.use( chaiHttp ) + +const crypto = require( 'crypto' ) +const { createUser } = require( `${appRoot}/modules/core/services/users` ) +const { createStream } = require( `${appRoot}/modules/core/services/streams` ) +const { createObjects } = require( `${appRoot}/modules/core/services/objects` ) +const { createCommitByBranchName, createCommitByBranchId } = require( `${appRoot}/modules/core/services/commits` ) + +const { getStreamHistory, getCommitHistory, getObjectHistory, getUserHistory, getTotalStreamCount, getTotalCommitCount, getTotalObjectCount, getTotalUserCount } = require( '../services' ) + +describe( 'Server stats services @stats-services', function() { + const params = { numUsers: 25, numStreams: 30, numObjects: 100, numCommits: 100 } + + before( async function() { + this.timeout( 10000 ) + + await knex.migrate.rollback( ) + await knex.migrate.latest( ) + + await init() + await seedDb( params ) + } ) + + after( async() => { + await knex.migrate.rollback( ) + } ) + + it( 'should return the total number of users on this server', async () => { + let res = await getTotalUserCount() + expect( res ).to.equal( params.numUsers ) + } ) + + it( 'should return the total number of streams on this server', async() => { + let res = await getTotalStreamCount() + expect( res ).to.equal( params.numStreams ) + } ) + + it( 'should return the total number of commits on this server', async() => { + let res = await getTotalCommitCount() + expect( res ).to.equal( params.numCommits ) + } ) + + it( 'should return the total number of objects on this server', async() => { + let res = await getTotalObjectCount() + expect( res ).to.equal( params.numObjects ) + } ) + + it( 'should return the stream creation history by month', async() => { + let res = await getStreamHistory() + expect( res ).to.be.an( 'array' ) + expect( res[0] ).to.have.property( 'count' ) + expect( res[0] ).to.have.property( 'created_month' ) + expect( res[0].count ).to.be.a( 'number' ) + expect( res[0].count ).to.equal( params.numStreams ) + } ) + + it( 'should return the commit creation history by month', async() => { + let res = await getCommitHistory() + expect( res ).to.be.an( 'array' ) + expect( res[0] ).to.have.property( 'count' ) + expect( res[0] ).to.have.property( 'created_month' ) + expect( res[0].count ).to.be.a( 'number' ) + expect( res[0].count ).to.equal( params.numCommits ) + } ) + + it( 'should return the object creation history by month', async() => { + let res = await getObjectHistory() + expect( res ).to.be.an( 'array' ) + expect( res[0] ).to.have.property( 'count' ) + expect( res[0] ).to.have.property( 'created_month' ) + expect( res[0].count ).to.be.a( 'number' ) + expect( res[0].count ).to.equal( params.numObjects ) + } ) + + it( 'should return the user creation history by month', async() => { + let res = await getUserHistory() + expect( res ).to.be.an( 'array' ) + expect( res[0] ).to.have.property( 'count' ) + expect( res[0] ).to.have.property( 'created_month' ) + expect( res[0].count ).to.be.a( 'number' ) + expect( res[0].count ).to.equal( params.numUsers ) + } ) + +} ) + +async function seedDb( { numUsers = 10, numStreams = 10, numObjects = 10, numCommits = 10 } = {} ) { + let users = [] + let streams = [] + + // create users + for ( let i = 0; i < numUsers; i++ ) { + let id = await createUser( { name: `User ${i}`, password: `SuperSecure${i}${i*3.14}`, email: `user${i}@speckle.systems` } ) + users.push( id ) + } + + // create streams + for ( let i = 0; i < numStreams; i++ ) { + let id = await createStream( { name: `Stream ${i}`, ownerId: users[i >= users.length ? users.length - 1 : i ] } ) + streams.push( id ) + } + + // create a objects + let mockObjects = createManyObjects( numObjects - 1 ) + let objs = await createObjects( streams[ 0 ], mockObjects ) + let commits = [] + + // create commits referencing those objects + for ( let i = 0; i < numCommits; i++ ) { + let id = await createCommitByBranchName( { + streamId: streams[0], + branchName: 'main', + sourceApplication: 'tests', + objectId: objs[ i >= objs.length ? objs.length - 1 : i ] + } ) + commits.push( id ) + } + +} + +function createManyObjects( num, noise ) { + num = num || 10000 + noise = noise || Math.random( ) * 100 + + let objs = [ ] + + let base = { name: 'base bastard 2', noise: noise, __closure: {} } + objs.push( base ) + let k = 0 + + for ( let i = 0; i < num; i++ ) { + let baby = { name: `mr. ${i}`, nest: { duck: i % 2 === 0, mallard: 'falsey', arr: [ i + 42, i, i ] } } + getAnId( baby ) + base.__closure[ baby.id ] = 1 + objs.push( baby ) + } + getAnId( base ) + return objs +} + +function getAnId( obj ) { + obj.id = obj.id || crypto.createHash( 'md5' ).update( JSON.stringify( obj ) ).digest( 'hex' ) +}