| 
									
										
										
										
											2024-05-14 12:06:59 -04:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const nconf = require('nconf'); | 
					
						
							|  |  |  | const assert = require('assert'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const db = require('../../src/database'); | 
					
						
							|  |  |  | const controllers = require('../../src/controllers'); | 
					
						
							|  |  |  | const middleware = require('../../src/middleware'); | 
					
						
							|  |  |  | const activitypub = require('../../src/activitypub'); | 
					
						
							|  |  |  | const utils = require('../../src/utils'); | 
					
						
							|  |  |  | const user = require('../../src/user'); | 
					
						
							|  |  |  | const categories = require('../../src/categories'); | 
					
						
							|  |  |  | const topics = require('../../src/topics'); | 
					
						
							|  |  |  | const analytics = require('../../src/analytics'); | 
					
						
							|  |  |  | const api = require('../../src/api'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | describe('Analytics', () => { | 
					
						
							|  |  |  | 	let cid; | 
					
						
							|  |  |  | 	let uid; | 
					
						
							|  |  |  | 	let postData; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	before(async () => { | 
					
						
							|  |  |  | 		nconf.set('runJobs', 1); | 
					
						
							|  |  |  | 		({ cid } = await categories.create({ name: utils.generateUUID().slice(0, 8) })); | 
					
						
							|  |  |  | 		const remoteUser = { | 
					
						
							|  |  |  | 			'@context': 'https://www.w3.org/ns/activitystreams', | 
					
						
							|  |  |  | 			id: 'https://example.org/user/foobar', | 
					
						
							|  |  |  | 			url: 'https://example.org/user/foobar', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			type: 'Person', | 
					
						
							|  |  |  | 			name: 'Foo Bar', | 
					
						
							|  |  |  | 			preferredUsername: 'foobar', | 
					
						
							|  |  |  | 			publicKey: { | 
					
						
							|  |  |  | 				id: 'https://example.org/user/foobar#key', | 
					
						
							|  |  |  | 				owner: 'https://example.org/user/foobar', | 
					
						
							|  |  |  | 				publicKeyPem: 'publickey', | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		activitypub._cache.set(`0;https://example.org/user/foobar`, remoteUser); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	after(async () => { | 
					
						
							|  |  |  | 		nconf.set('runJobs', undefined); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	beforeEach(async () => { | 
					
						
							|  |  |  | 		uid = await user.create({ username: utils.generateUUID().slice(0, 8) }); | 
					
						
							|  |  |  | 		({ postData } = await topics.post({ | 
					
						
							|  |  |  | 			uid, | 
					
						
							|  |  |  | 			cid, | 
					
						
							|  |  |  | 			title: utils.generateUUID(), | 
					
						
							|  |  |  | 			content: utils.generateUUID(), | 
					
						
							|  |  |  | 		})); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	it('should record the incoming activity if successfully processed', async () => { | 
					
						
							|  |  |  | 		const id = `https://example.org/activity/${utils.generateUUID()}`; | 
					
						
							|  |  |  | 		await controllers.activitypub.postInbox({ | 
					
						
							|  |  |  | 			body: { | 
					
						
							|  |  |  | 				id, | 
					
						
							|  |  |  | 				type: 'Like', | 
					
						
							|  |  |  | 				actor: 'https://example.org/user/foobar', | 
					
						
							|  |  |  | 				object: { | 
					
						
							|  |  |  | 					type: 'Note', | 
					
						
							|  |  |  | 					id: `${nconf.get('url')}/post/${postData.pid}`, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, { sendStatus: () => {} }); | 
					
						
							|  |  |  | 		const processed = await db.isSortedSetMember('activities:datetime', id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		assert(processed); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	it('should not process the activity if received again', async () => { | 
					
						
							|  |  |  | 		// Specifically, the controller would update the score, but the request should be caught in middlewares and ignored
 | 
					
						
							|  |  |  | 		const id = `https://example.org/activity/${utils.generateUUID()}`; | 
					
						
							|  |  |  | 		await controllers.activitypub.postInbox({ | 
					
						
							|  |  |  | 			body: { | 
					
						
							|  |  |  | 				id, | 
					
						
							|  |  |  | 				type: 'Like', | 
					
						
							|  |  |  | 				actor: 'https://example.org/user/foobar', | 
					
						
							|  |  |  | 				object: { | 
					
						
							|  |  |  | 					type: 'Note', | 
					
						
							|  |  |  | 					id: `${nconf.get('url')}/post/${postData.pid}`, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, { sendStatus: () => {} }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-07 14:04:46 -04:00
										 |  |  | 		await middleware.activitypub.assertPayload({ | 
					
						
							| 
									
										
										
										
											2024-05-14 12:06:59 -04:00
										 |  |  | 			body: { | 
					
						
							|  |  |  | 				id, | 
					
						
							|  |  |  | 				type: 'Like', | 
					
						
							|  |  |  | 				actor: 'https://example.org/user/foobar', | 
					
						
							|  |  |  | 				object: { | 
					
						
							|  |  |  | 					type: 'Note', | 
					
						
							|  |  |  | 					id: `${nconf.get('url')}/post/${postData.pid}`, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			sendStatus: (statusCode) => { | 
					
						
							|  |  |  | 				assert.strictEqual(statusCode, 200); | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	it('should increment the last seen time of that domain', async () => { | 
					
						
							|  |  |  | 		const id = `https://example.org/activity/${utils.generateUUID()}`; | 
					
						
							|  |  |  | 		const before = await db.sortedSetScore('domains:lastSeen', 'example.org'); | 
					
						
							|  |  |  | 		await controllers.activitypub.postInbox({ | 
					
						
							|  |  |  | 			body: { | 
					
						
							|  |  |  | 				id, | 
					
						
							|  |  |  | 				type: 'Like', | 
					
						
							|  |  |  | 				actor: 'https://example.org/user/foobar', | 
					
						
							|  |  |  | 				object: { | 
					
						
							|  |  |  | 					type: 'Note', | 
					
						
							|  |  |  | 					id: `${nconf.get('url')}/post/${postData.pid}`, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, { sendStatus: () => {} }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const after = await db.sortedSetScore('domains:lastSeen', 'example.org'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		assert(before && after); | 
					
						
							|  |  |  | 		assert(before < after); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	it('should increment various metrics', async () => { | 
					
						
							|  |  |  | 		let counters; | 
					
						
							|  |  |  | 		({ counters } = analytics.peek()); | 
					
						
							|  |  |  | 		const before = { ...counters }; | 
					
						
							| 
									
										
										
										
											2025-02-18 10:53:25 -05:00
										 |  |  | 		const { setTimeout } = require('timers/promises'); | 
					
						
							| 
									
										
										
										
											2024-05-14 12:06:59 -04:00
										 |  |  | 		const id = `https://example.org/activity/${utils.generateUUID()}`; | 
					
						
							|  |  |  | 		await controllers.activitypub.postInbox({ | 
					
						
							|  |  |  | 			body: { | 
					
						
							|  |  |  | 				id, | 
					
						
							|  |  |  | 				type: 'Like', | 
					
						
							|  |  |  | 				actor: 'https://example.org/user/foobar', | 
					
						
							|  |  |  | 				object: { | 
					
						
							|  |  |  | 					type: 'Note', | 
					
						
							|  |  |  | 					id: `${nconf.get('url')}/post/${postData.pid}`, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, { sendStatus: () => {} }); | 
					
						
							| 
									
										
										
										
											2025-02-18 10:53:25 -05:00
										 |  |  | 		await setTimeout(2000); | 
					
						
							| 
									
										
										
										
											2024-05-14 12:06:59 -04:00
										 |  |  | 		({ counters } = analytics.peek()); | 
					
						
							|  |  |  | 		const after = { ...counters }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const metrics = ['activities', 'activities:byType:Like', 'activities:byHost:example.org']; | 
					
						
							|  |  |  | 		metrics.forEach((metric) => { | 
					
						
							|  |  |  | 			assert(before[metric] && after[metric]); | 
					
						
							|  |  |  | 			assert(before[metric] < after[metric]); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | }); |