| 
									
										
										
										
											2023-05-22 23:38:11 -04:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 16:13:16 -04:00
										 |  |  | const assert = require('assert'); | 
					
						
							| 
									
										
										
										
											2023-05-22 23:38:11 -04:00
										 |  |  | const nconf = require('nconf'); | 
					
						
							| 
									
										
										
										
											2024-04-26 11:30:08 -04:00
										 |  |  | const path = require('path'); | 
					
						
							| 
									
										
										
										
											2023-05-22 23:38:11 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | const db = require('./mocks/databasemock'); | 
					
						
							| 
									
										
										
										
											2023-05-23 16:13:16 -04:00
										 |  |  | const slugify = require('../src/slugify'); | 
					
						
							|  |  |  | const utils = require('../src/utils'); | 
					
						
							| 
									
										
										
										
											2023-12-21 14:38:16 -05:00
										 |  |  | const request = require('../src/request'); | 
					
						
							| 
									
										
										
										
											2023-05-23 16:13:16 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-26 11:30:08 -04:00
										 |  |  | const file = require('../src/file'); | 
					
						
							| 
									
										
										
										
											2024-02-26 14:22:35 -05:00
										 |  |  | const install = require('../src/install'); | 
					
						
							| 
									
										
										
										
											2023-05-24 14:00:41 -04:00
										 |  |  | const meta = require('../src/meta'); | 
					
						
							| 
									
										
										
										
											2023-05-23 16:13:16 -04:00
										 |  |  | const user = require('../src/user'); | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | const categories = require('../src/categories'); | 
					
						
							|  |  |  | const topics = require('../src/topics'); | 
					
						
							| 
									
										
										
										
											2024-05-07 12:15:51 -04:00
										 |  |  | const posts = require('../src/posts'); | 
					
						
							| 
									
										
										
										
											2023-06-21 15:45:29 -04:00
										 |  |  | const activitypub = require('../src/activitypub'); | 
					
						
							| 
									
										
										
										
											2023-05-22 23:38:11 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | describe('ActivityPub integration', () => { | 
					
						
							| 
									
										
										
										
											2024-02-26 14:22:35 -05:00
										 |  |  | 	before(async () => { | 
					
						
							| 
									
										
										
										
											2023-06-16 10:57:34 -04:00
										 |  |  | 		meta.config.activitypubEnabled = 1; | 
					
						
							| 
									
										
										
										
											2025-02-28 13:56:33 -05:00
										 |  |  | 		meta.config.activitypubAllowLoopback = 1; | 
					
						
							| 
									
										
										
										
											2024-02-26 14:22:35 -05:00
										 |  |  | 		await install.giveWorldPrivileges(); | 
					
						
							| 
									
										
										
										
											2023-05-24 14:00:41 -04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	after(() => { | 
					
						
							| 
									
										
										
										
											2023-06-16 10:57:34 -04:00
										 |  |  | 		delete meta.config.activitypubEnabled; | 
					
						
							| 
									
										
										
										
											2023-05-24 14:00:41 -04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-02 21:58:46 -05:00
										 |  |  | 	describe('Outgoing AP logging for test runner', () => { | 
					
						
							| 
									
										
										
										
											2025-02-28 13:56:33 -05:00
										 |  |  | 		it('should log an entry in ActivityPub._sent when .send is called', async () => { | 
					
						
							|  |  |  | 			const uuid = utils.generateUUID(); | 
					
						
							|  |  |  | 			const uid = await user.create({ username: uuid }); | 
					
						
							|  |  |  | 			await activitypub.send('uid', 0, [`https://localhost/uid/${uid}`], { id: `${nconf.get('url')}/activity/${uuid}`, foo: 'bar' }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-02 22:09:12 -05:00
										 |  |  | 			assert(activitypub._sent.has(`${nconf.get('url')}/activity/${uuid}`)); | 
					
						
							| 
									
										
										
										
											2025-02-28 13:56:33 -05:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-21 20:47:55 -05:00
										 |  |  | 	describe('Master toggle', () => { | 
					
						
							| 
									
										
										
										
											2025-01-20 12:05:17 -05:00
										 |  |  | 		before(async () => { | 
					
						
							|  |  |  | 			delete meta.config.activitypubEnabled; | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('calls to activitypub.get should throw', async () => { | 
					
						
							|  |  |  | 			await assert.rejects( | 
					
						
							|  |  |  | 				activitypub.get('uid', 0, 'https://example.org'), | 
					
						
							|  |  |  | 				{ message: '[[error:activitypub.not-enabled]]' }, | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('calls to activitypub.send should silently log', async () => { | 
					
						
							|  |  |  | 			await activitypub.send('uid', 0, ['https://example.org'], { foo: 'bar' }); | 
					
						
							| 
									
										
										
										
											2025-01-20 13:25:05 -05:00
										 |  |  | 			assert.strictEqual(activitypub.helpers.log(), '[activitypub/send] Federation not enabled; not sending.'); | 
					
						
							| 
									
										
										
										
											2025-01-20 12:05:17 -05:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('request for an activitypub route should return 404 Not Found', async () => { | 
					
						
							|  |  |  | 			const uid = user.create({ username: utils.generateUUID() }); | 
					
						
							|  |  |  | 			const { response } = await request.get(`${nconf.get('url')}/uid/${uid}`, { | 
					
						
							|  |  |  | 				headers: { | 
					
						
							|  |  |  | 					Accept: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			assert.strictEqual(response.statusCode, 404); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('requests to the /ap endpoint should return 404 Not Found', async () => { | 
					
						
							|  |  |  | 			const { response } = await request.get(`${nconf.get('url')}/ap?resource=${encodeURIComponent('https://example.org')}`); | 
					
						
							|  |  |  | 			assert.strictEqual(response.statusCode, 404); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-20 13:42:10 -05:00
										 |  |  | 		it('webfinger request to a local user should not indicate an application/activity+json endpoint', async () => { | 
					
						
							| 
									
										
										
										
											2025-01-20 12:05:17 -05:00
										 |  |  | 			const username = utils.generateUUID().slice(0, 8); | 
					
						
							| 
									
										
										
										
											2025-01-20 13:42:10 -05:00
										 |  |  | 			await user.create({ username }); | 
					
						
							|  |  |  | 			const { response, body } = await request.get(`${nconf.get('url')}/.well-known/webfinger?resource=acct%3a${username}%40${nconf.get('url_parsed').host}`); | 
					
						
							| 
									
										
										
										
											2025-01-20 12:05:17 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			assert.strictEqual(response.statusCode, 200); | 
					
						
							| 
									
										
										
										
											2025-01-20 13:42:10 -05:00
										 |  |  | 			assert(body && body.links && Array.isArray(body.links)); | 
					
						
							|  |  |  | 			assert(!body.links.some(obj => obj.type && obj.type === 'application/activity+json')); | 
					
						
							| 
									
										
										
										
											2025-01-20 12:05:17 -05:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		after(() => { | 
					
						
							|  |  |  | 			meta.config.activitypubEnabled = 1; | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-13 13:38:52 -05:00
										 |  |  | 	describe('Helpers', () => { | 
					
						
							| 
									
										
										
										
											2023-12-11 14:35:04 -05:00
										 |  |  | 		describe('.query()', () => { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		describe('.generateKeys()', () => { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-21 13:43:56 -05:00
										 |  |  | 		describe('.resolveId()', () => { | 
					
						
							| 
									
										
										
										
											2024-02-21 10:58:20 -05:00
										 |  |  | 			let url; | 
					
						
							|  |  |  | 			let resolved; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			before(() => { | 
					
						
							|  |  |  | 				url = 'https://example.org/topic/foobar'; | 
					
						
							|  |  |  | 				resolved = 'https://example.org/tid/1234'; | 
					
						
							|  |  |  | 				activitypub._cache.set(`0;${url}`, { | 
					
						
							|  |  |  | 					id: resolved, | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should return the resolved id when queried', async () => { | 
					
						
							|  |  |  | 				const id = await activitypub.resolveId(0, url); | 
					
						
							|  |  |  | 				assert.strictEqual(id, resolved); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should return null when the query fails', async () => { | 
					
						
							|  |  |  | 				const id = await activitypub.resolveId(0, 'https://example.org/sdlknsdfnsd'); | 
					
						
							|  |  |  | 				assert.strictEqual(id, null); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should return null when the resolved host does not match the queried host', async () => { | 
					
						
							|  |  |  | 				const url = 'https://example.com/topic/foobar'; // .com attempting to overwrite .org data
 | 
					
						
							|  |  |  | 				const resolved = 'https://example.org/tid/1234'; // .org
 | 
					
						
							|  |  |  | 				activitypub._cache.set(`0;${url}`, { | 
					
						
							|  |  |  | 					id: resolved, | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				const id = await activitypub.resolveId(0, url); | 
					
						
							|  |  |  | 				assert.strictEqual(id, null); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 		describe('.resolveLocalId()', () => { | 
					
						
							| 
									
										
										
										
											2023-12-11 14:35:04 -05:00
										 |  |  | 			let uid; | 
					
						
							|  |  |  | 			let slug; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			beforeEach(async () => { | 
					
						
							|  |  |  | 				slug = slugify(utils.generateUUID().slice(0, 8)); | 
					
						
							|  |  |  | 				uid = await user.create({ username: slug }); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-21 10:26:26 -05:00
										 |  |  | 			it('should return null when an invalid input is passed in', async () => { | 
					
						
							|  |  |  | 				const { type, id } = await activitypub.helpers.resolveLocalId('ncl28h3qwhoiclwnevoinw3u'); | 
					
						
							|  |  |  | 				assert.strictEqual(type, null); | 
					
						
							|  |  |  | 				assert.strictEqual(id, null); | 
					
						
							| 
									
										
										
										
											2023-12-11 14:35:04 -05:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should return null when valid input is passed but does not resolve', async () => { | 
					
						
							| 
									
										
										
										
											2024-04-25 12:00:31 +02:00
										 |  |  | 				const { type, id } = await activitypub.helpers.resolveLocalId(`acct%3afoobar@${nconf.get('url_parsed').host}`); | 
					
						
							| 
									
										
										
										
											2024-02-21 10:26:26 -05:00
										 |  |  | 				assert.strictEqual(type, 'user'); | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 				assert.strictEqual(id, null); | 
					
						
							| 
									
										
										
										
											2023-12-11 14:35:04 -05:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should resolve to a local uid when given a webfinger-style string', async () => { | 
					
						
							| 
									
										
										
										
											2024-04-25 12:00:31 +02:00
										 |  |  | 				const { id } = await activitypub.helpers.resolveLocalId(`acct%3a${slug}@${nconf.get('url_parsed').host}`); | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 				assert.strictEqual(id, uid); | 
					
						
							| 
									
										
										
										
											2023-12-11 14:35:04 -05:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should resolve even without the "acct:" prefix', async () => { | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 				const { id } = await activitypub.helpers.resolveLocalId(`${slug}@${nconf.get('url_parsed').host}`); | 
					
						
							|  |  |  | 				assert.strictEqual(id, uid); | 
					
						
							| 
									
										
										
										
											2023-12-11 14:35:04 -05:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should resolve when passed a full URL', async () => { | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 				const { id } = await activitypub.helpers.resolveLocalId(`${nconf.get('url')}/user/${slug}`); | 
					
						
							|  |  |  | 				assert.strictEqual(id, uid); | 
					
						
							| 
									
										
										
										
											2023-12-11 14:35:04 -05:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2024-04-29 16:16:07 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-21 20:47:55 -05:00
										 |  |  | 		describe('.remoteAnchorToLocalProfile', () => { | 
					
						
							| 
									
										
										
										
											2025-01-15 14:19:39 -05:00
										 |  |  | 			const uuid1 = utils.generateUUID(); | 
					
						
							|  |  |  | 			const id1 = `https://example.org/uuid/${uuid1}`; | 
					
						
							|  |  |  | 			const url1 = `https://example.org/test`; | 
					
						
							|  |  |  | 			const uuid2 = utils.generateUUID(); | 
					
						
							|  |  |  | 			const id2 = `https://example.org/uuid/${uuid2}`; | 
					
						
							|  |  |  | 			const localUsername = utils.generateUUID(); | 
					
						
							|  |  |  | 			const localSlug = slugify(localUsername); | 
					
						
							|  |  |  | 			let localUid; | 
					
						
							|  |  |  | 			before(async () => { | 
					
						
							|  |  |  | 				// Mock up a fake remote user
 | 
					
						
							|  |  |  | 				[,,,, localUid] = await Promise.all([ | 
					
						
							|  |  |  | 					db.setObjectField('remoteUrl:uid', url1, id1), | 
					
						
							|  |  |  | 					db.sortedSetAdd('usersRemote:lastCrawled', Date.now(), id2), | 
					
						
							|  |  |  | 					db.setObject(`userRemote:${id1}`, { uid: id1, userslug: uuid1 }), | 
					
						
							|  |  |  | 					db.setObject(`userRemote:${id2}`, { uid: id2, userslug: id2 }), | 
					
						
							|  |  |  | 					user.create({ username: localUsername }), | 
					
						
							|  |  |  | 				]); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should convert an anchor pointing to a remote user URL', async () => { | 
					
						
							|  |  |  | 				const content = `adsf <a href="${url1}">@${uuid1}</a> asdf`; | 
					
						
							|  |  |  | 				const converted = await activitypub.helpers.remoteAnchorToLocalProfile(content); | 
					
						
							|  |  |  | 				assert.strictEqual(converted, `adsf <a href="/user/${uuid1}">@${uuid1}</a> asdf`); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should convert an anchor pointing to a remote user id', async () => { | 
					
						
							|  |  |  | 				const content = `adsf <a href="${id2}">@${uuid2}</a> asdf`; | 
					
						
							|  |  |  | 				const converted = await activitypub.helpers.remoteAnchorToLocalProfile(content); | 
					
						
							|  |  |  | 				assert.strictEqual(converted, `adsf <a href="/user/${encodeURIComponent(id2)}">@${uuid2}</a> asdf`); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should convert an anchor pointing to a local user URL', async () => { | 
					
						
							|  |  |  | 				const content = `adsf <a href="${nconf.get('url')}/user/${localSlug}">@${localSlug}</a> asdf`; | 
					
						
							|  |  |  | 				const converted = await activitypub.helpers.remoteAnchorToLocalProfile(content); | 
					
						
							|  |  |  | 				assert.strictEqual(converted, `adsf <a href="/user/${localSlug}">@${localSlug}</a> asdf`); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should convert an anchor pointing to a local user URL', async () => { | 
					
						
							|  |  |  | 				const content = `adsf <a href="${nconf.get('url')}/uid/${localUid}">@${localSlug}</a> asdf`; | 
					
						
							|  |  |  | 				const converted = await activitypub.helpers.remoteAnchorToLocalProfile(content); | 
					
						
							|  |  |  | 				assert.strictEqual(converted, `adsf <a href="/user/${localSlug}">@${localSlug}</a> asdf`); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			after(async () => { | 
					
						
							|  |  |  | 				await Promise.all([ | 
					
						
							|  |  |  | 					db.deleteObjectField('remoteUrl:uid', url1), | 
					
						
							|  |  |  | 					db.sortedSetRemove('usersRemote:lastCrawled', id2), | 
					
						
							|  |  |  | 				]); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2023-12-11 14:35:04 -05:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 16:13:16 -04:00
										 |  |  | 	describe('ActivityPub screener middleware', () => { | 
					
						
							|  |  |  | 		let uid; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		beforeEach(async () => { | 
					
						
							| 
									
										
										
										
											2024-02-05 14:12:23 -05:00
										 |  |  | 			uid = await user.create({ username: slugify(utils.generateUUID().slice(0, 8)) }); | 
					
						
							| 
									
										
										
										
											2023-05-23 16:13:16 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-24 14:00:41 -04:00
										 |  |  | 		it('should return regular user profile html if federation is disabled', async () => { | 
					
						
							| 
									
										
										
										
											2023-06-16 10:57:34 -04:00
										 |  |  | 			delete meta.config.activitypubEnabled; | 
					
						
							| 
									
										
										
										
											2023-05-24 14:00:41 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-05 14:12:23 -05:00
										 |  |  | 			const { response, body } = await request.get(`${nconf.get('url')}/uid/${uid}`, { | 
					
						
							| 
									
										
										
										
											2023-05-24 14:00:41 -04:00
										 |  |  | 				headers: { | 
					
						
							|  |  |  | 					Accept: 'text/html', | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			assert(response); | 
					
						
							|  |  |  | 			assert.strictEqual(response.statusCode, 200); | 
					
						
							| 
									
										
										
										
											2023-12-21 14:38:16 -05:00
										 |  |  | 			assert(body.startsWith('<!DOCTYPE html>')); | 
					
						
							| 
									
										
										
										
											2023-05-24 14:00:41 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-16 10:57:34 -04:00
										 |  |  | 			meta.config.activitypubEnabled = 1; | 
					
						
							| 
									
										
										
										
											2023-05-24 14:00:41 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 16:13:16 -04:00
										 |  |  | 		it('should return regular user profile html if Accept header is not ActivityPub-related', async () => { | 
					
						
							| 
									
										
										
										
											2024-02-05 14:12:23 -05:00
										 |  |  | 			const { response, body } = await request.get(`${nconf.get('url')}/uid/${uid}`, { | 
					
						
							| 
									
										
										
										
											2023-05-23 16:13:16 -04:00
										 |  |  | 				headers: { | 
					
						
							|  |  |  | 					Accept: 'text/html', | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			assert(response); | 
					
						
							|  |  |  | 			assert.strictEqual(response.statusCode, 200); | 
					
						
							| 
									
										
										
										
											2023-12-21 14:38:16 -05:00
										 |  |  | 			assert(body.startsWith('<!DOCTYPE html>')); | 
					
						
							| 
									
										
										
										
											2023-05-23 16:13:16 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should return the ActivityPub Actor JSON-LD payload if the correct Accept header is provided', async () => { | 
					
						
							| 
									
										
										
										
											2024-02-05 14:12:23 -05:00
										 |  |  | 			const { response, body } = await request.get(`${nconf.get('url')}/uid/${uid}`, { | 
					
						
							| 
									
										
										
										
											2023-05-22 23:38:11 -04:00
										 |  |  | 				headers: { | 
					
						
							| 
									
										
										
										
											2023-05-23 16:13:16 -04:00
										 |  |  | 					Accept: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', | 
					
						
							| 
									
										
										
										
											2023-05-22 23:38:11 -04:00
										 |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2023-05-23 16:13:16 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			assert(response); | 
					
						
							|  |  |  | 			assert.strictEqual(response.statusCode, 200); | 
					
						
							| 
									
										
										
										
											2023-12-21 14:38:16 -05:00
										 |  |  | 			assert(body.hasOwnProperty('@context')); | 
					
						
							|  |  |  | 			assert(body['@context'].includes('https://www.w3.org/ns/activitystreams')); | 
					
						
							| 
									
										
										
										
											2023-05-23 16:13:16 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-05 14:12:23 -05:00
										 |  |  | 	describe('Receipt of ActivityPub events to inboxes (federating IN)', () => { | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | 		describe('Create', () => { | 
					
						
							|  |  |  | 			describe('Note', () => { | 
					
						
							| 
									
										
										
										
											2024-02-21 10:26:26 -05:00
										 |  |  | 				const slug = utils.generateUUID(); | 
					
						
							|  |  |  | 				const id = `https://example.org/status/${slug}`; | 
					
						
							|  |  |  | 				const remoteNote = { | 
					
						
							|  |  |  | 					'@context': 'https://www.w3.org/ns/activitystreams', | 
					
						
							|  |  |  | 					id, | 
					
						
							|  |  |  | 					url: id, | 
					
						
							|  |  |  | 					type: 'Note', | 
					
						
							|  |  |  | 					to: ['https://www.w3.org/ns/activitystreams#Public'], | 
					
						
							|  |  |  | 					cc: ['https://example.org/user/foobar/followers'], | 
					
						
							|  |  |  | 					inReplyTo: null, | 
					
						
							|  |  |  | 					attributedTo: 'https://example.org/user/foobar', | 
					
						
							|  |  |  | 					name: 'Foo Bar', | 
					
						
							|  |  |  | 					content: '<b>Baz quux</b>', | 
					
						
							|  |  |  | 					published: new Date().toISOString(), | 
					
						
							|  |  |  | 					source: { | 
					
						
							|  |  |  | 						content: '**Baz quux**', | 
					
						
							|  |  |  | 						mediaType: 'text/markdown', | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}; | 
					
						
							| 
									
										
										
										
											2024-03-12 12:03:16 -04:00
										 |  |  | 				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', | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}; | 
					
						
							| 
									
										
										
										
											2024-02-21 10:26:26 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | 				let topic; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				before(async () => { | 
					
						
							| 
									
										
										
										
											2024-02-21 10:26:26 -05:00
										 |  |  | 					const controllers = require('../src/controllers'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					activitypub._cache.set(`0;${id}`, remoteNote); | 
					
						
							| 
									
										
										
										
											2024-03-12 12:03:16 -04:00
										 |  |  | 					activitypub._cache.set(`0;https://example.org/user/foobar`, remoteUser); | 
					
						
							| 
									
										
										
										
											2024-04-03 13:49:27 -04:00
										 |  |  | 					await db.sortedSetAdd(`followersRemote:${remoteUser.id}`, Date.now(), 1); // fake a follow
 | 
					
						
							| 
									
										
										
										
											2024-02-21 10:26:26 -05:00
										 |  |  | 					await controllers.activitypub.postInbox({ | 
					
						
							|  |  |  | 						body: { | 
					
						
							|  |  |  | 							type: 'Create', | 
					
						
							|  |  |  | 							actor: 'https://example.org/user/foobar', | 
					
						
							|  |  |  | 							object: remoteNote, | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 					}, { sendStatus: () => {} }); | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | 				}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				it('should create a new topic if Note is at root-level or its parent has not been seen before', async () => { | 
					
						
							| 
									
										
										
										
											2024-02-21 10:26:26 -05:00
										 |  |  | 					const saved = await db.getObject(`post:${id}`); | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					assert(saved); | 
					
						
							|  |  |  | 					assert(saved.tid); | 
					
						
							| 
									
										
										
										
											2024-02-21 10:26:26 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					topic = await topics.getTopicData(saved.tid); | 
					
						
							| 
									
										
										
										
											2025-01-07 12:20:46 -05:00
										 |  |  | 					const { uid, mainPid } = topic; | 
					
						
							|  |  |  | 					assert(uid && mainPid); | 
					
						
							|  |  |  | 					const { content, sourceContent } = await posts.getPostData(mainPid); | 
					
						
							|  |  |  | 					assert.strictEqual(uid, 'https://example.org/user/foobar'); | 
					
						
							|  |  |  | 					assert.strictEqual(content, ''); | 
					
						
							|  |  |  | 					assert.strictEqual(sourceContent, '**Baz quux**'); | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | 				}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				it('should properly save the topic title in the topic hash', async () => { | 
					
						
							| 
									
										
										
										
											2024-02-21 10:26:26 -05:00
										 |  |  | 					assert.strictEqual(topic.title, 'Foo Bar'); | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | 				}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				it('should properly save the mainPid in the topic hash', async () => { | 
					
						
							| 
									
										
										
										
											2024-02-21 10:26:26 -05:00
										 |  |  | 					assert.strictEqual(topic.mainPid, id); | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2024-01-26 22:35:02 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				// todo: test topic replies, too
 | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2024-02-05 14:12:23 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-14 12:06:59 -04:00
										 |  |  | 	describe('Serving of local assets to remote clients (mocking)', () => { | 
					
						
							| 
									
										
										
										
											2024-05-09 15:48:58 -04:00
										 |  |  | 		describe('Note', () => { | 
					
						
							| 
									
										
										
										
											2024-05-07 11:56:58 -04:00
										 |  |  | 			let cid; | 
					
						
							|  |  |  | 			let uid; | 
					
						
							| 
									
										
										
										
											2024-02-05 14:12:23 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			before(async () => { | 
					
						
							| 
									
										
										
										
											2024-05-07 11:56:58 -04:00
										 |  |  | 				({ cid } = await categories.create({ name: utils.generateUUID().slice(0, 8) })); | 
					
						
							|  |  |  | 				const slug = slugify(utils.generateUUID().slice(0, 8)); | 
					
						
							|  |  |  | 				uid = await user.create({ username: slug }); | 
					
						
							| 
									
										
										
										
											2024-02-05 14:12:23 -05:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-07 11:56:58 -04:00
										 |  |  | 			describe('Existing and resolvable', () => { | 
					
						
							|  |  |  | 				let body; | 
					
						
							|  |  |  | 				let response; | 
					
						
							|  |  |  | 				let postData; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				before(async () => { | 
					
						
							|  |  |  | 					({ postData } = await topics.post({ | 
					
						
							|  |  |  | 						uid, | 
					
						
							|  |  |  | 						cid, | 
					
						
							|  |  |  | 						title: 'Lorem "Lipsum" Ipsum', | 
					
						
							|  |  |  | 						content: 'Lorem ipsum dolor sit amet', | 
					
						
							|  |  |  | 					})); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					({ body, response } = await request.get(`${nconf.get('url')}/post/${postData.pid}`, { | 
					
						
							|  |  |  | 						headers: { | 
					
						
							|  |  |  | 							Accept: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 					})); | 
					
						
							| 
									
										
										
										
											2024-02-05 14:12:23 -05:00
										 |  |  | 				}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-07 11:56:58 -04:00
										 |  |  | 				it('should return a 404 on a non-existant post', async () => { | 
					
						
							|  |  |  | 					const { response } = await request.get(`${nconf.get('url')}/post/${parseInt(postData.pid, 10) + 1}`, { | 
					
						
							|  |  |  | 						headers: { | 
					
						
							|  |  |  | 							Accept: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2024-02-05 14:12:23 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-07 11:56:58 -04:00
										 |  |  | 					assert.strictEqual(response.statusCode, 404); | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2024-02-05 14:12:23 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-07 11:56:58 -04:00
										 |  |  | 				it('should return a 200 response on an existing post', () => { | 
					
						
							|  |  |  | 					assert.strictEqual(response.statusCode, 200); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				it('should return the expected Content-Type header', () => { | 
					
						
							|  |  |  | 					assert.strictEqual(response.headers['content-type'], 'application/activity+json; charset=utf-8'); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				it('Topic title (`name`) should not be escaped', () => { | 
					
						
							|  |  |  | 					assert.strictEqual(body.name, 'Lorem "Lipsum" Ipsum'); | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2024-02-05 14:12:23 -05:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2024-04-26 11:30:08 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-07 11:56:58 -04:00
										 |  |  | 			describe('Soft deleted', () => { | 
					
						
							| 
									
										
										
										
											2024-05-07 12:15:51 -04:00
										 |  |  | 				let body; | 
					
						
							|  |  |  | 				let response; | 
					
						
							|  |  |  | 				let postData; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				before(async () => { | 
					
						
							|  |  |  | 					({ postData } = await topics.post({ | 
					
						
							|  |  |  | 						uid, | 
					
						
							|  |  |  | 						cid, | 
					
						
							|  |  |  | 						title: utils.generateUUID(), | 
					
						
							|  |  |  | 						content: utils.generateUUID(), | 
					
						
							|  |  |  | 					})); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					await posts.delete(postData.pid, uid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					({ body, response } = await request.get(`${nconf.get('url')}/post/${postData.pid}`, { | 
					
						
							|  |  |  | 						headers: { | 
					
						
							|  |  |  | 							Accept: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 					})); | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2024-05-07 11:56:58 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-07 12:15:51 -04:00
										 |  |  | 				it('should return a 200 response on an existing post', () => { | 
					
						
							|  |  |  | 					assert.strictEqual(response.statusCode, 200); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				it('should return a Tombstone object', () => { | 
					
						
							|  |  |  | 					assert.strictEqual(body.type, 'Tombstone'); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				it('should still retain the existing id and former type', () => { | 
					
						
							|  |  |  | 					assert.strictEqual(body.id, `${nconf.get('url')}/post/${postData.pid}`); | 
					
						
							|  |  |  | 					assert.strictEqual(body.formerType, 'Note'); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				it('should still contain contextual information (context, audience, attributedTo)', () => { | 
					
						
							|  |  |  | 					assert(['context', 'audience', 'attributedTo'].every(prop => body.hasOwnProperty(prop) && body[prop])); | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2024-04-26 11:30:08 -04:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2024-02-05 14:12:23 -05:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2024-04-03 13:49:27 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-14 12:13:23 -04:00
										 |  |  | 	describe('ActivityPub', async () => { | 
					
						
							| 
									
										
										
										
											2024-04-26 11:30:08 -04:00
										 |  |  | 		let files; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		before(async () => { | 
					
						
							|  |  |  | 			files = await file.walk(path.resolve(__dirname, './activitypub')); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('subfolder tests', () => { | 
					
						
							|  |  |  | 			files.forEach((filePath) => { | 
					
						
							|  |  |  | 				require(filePath); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2023-05-22 23:38:11 -04:00
										 |  |  | }); |