add replies to dummy data and test them in the ui tests

as part of this change, refactor the ui tests so that they
use lower level backend apis instead of simulating http requests
(this allows us to mock replies as well)
pull/132/head
Dario Piotrowicz 2023-01-18 13:40:09 +00:00
rodzic eea4599add
commit e464fe2bcd
4 zmienionych plików z 189 dodań i 41 usunięć

Wyświetl plik

@ -30,7 +30,7 @@ module.exports = {
'@typescript-eslint/ban-ts-comment': 'error',
'prefer-spread': 'error',
'no-case-declarations': 'error',
'no-console': 'error',
'no-console': ['error', { allow: ['warn', 'error']} ],
'@typescript-eslint/no-unused-vars': ['error'],
'prefer-const': 'error',
},

Wyświetl plik

@ -1,57 +1,71 @@
import { createPerson, getPersonByEmail, type Person } from 'wildebeest/backend/src/activitypub/actors'
import * as statusesAPI from 'wildebeest/functions/api/v1/statuses'
import * as reblogAPI from 'wildebeest/functions/api/v1/statuses/[id]/reblog'
import { statuses } from 'wildebeest/frontend/src/dummyData'
import { replies, statuses } from 'wildebeest/frontend/src/dummyData'
import type { Account, MastodonStatus } from 'wildebeest/frontend/src/types'
const kek = 'test-kek'
/* eslint-disable @typescript-eslint/no-empty-function */
const queue: unknown = {
send() {},
sendBatch() {},
}
/* eslint-disable @typescript-eslint/no-empty-function */
const cache: unknown = {
get() {},
put() {},
}
import { createPublicNote, Note } from 'wildebeest/backend/src/activitypub/objects/note'
import { addObjectInOutbox } from 'wildebeest/backend/src/activitypub/actors/outbox'
import { insertReblog } from 'wildebeest/backend/src/mastodon/reblog'
import { insertReply } from 'wildebeest/backend/src/mastodon/reply'
/**
* Run helper commands to initialize the database with actors, statuses, etc.
*/
export async function init(domain: string, db: D1Database) {
const loadedStatuses: MastodonStatus[] = []
const loadedStatuses: { status: MastodonStatus; note: Note }[] = []
for (const status of statuses) {
const actor = await getOrCreatePerson(domain, db, status.account)
loadedStatuses.push(await createStatus(db, actor, status.content))
const note = await createStatus(domain, db, actor, status.content)
loadedStatuses.push({ status, note })
}
// Grab the account from an arbitrary status to use as the reblogger
const rebloggerAccount = loadedStatuses[1].account
const reblogger = await getOrCreatePerson(domain, db, rebloggerAccount)
// Reblog an arbitrary status with this reblogger
const statusToReblog = loadedStatuses[2]
await reblogStatus(db, reblogger, statusToReblog, domain)
const { reblogger, noteToReblog } = await pickReblogDetails(loadedStatuses, domain, db)
reblogNote(db, reblogger, noteToReblog)
for (const reply of replies) {
await createReply(domain, db, reply, loadedStatuses)
}
}
/**
* Create a status object in the given actors outbox.
* Create a status object in the given actor's outbox.
*/
async function createStatus(db: D1Database, actor: Person, status: string, visibility = 'public') {
const body = {
status,
visibility,
async function createStatus(domain: string, db: D1Database, actor: Person, content: string) {
const note = await createPublicNote(domain, db, content, actor)
await addObjectInOutbox(db, actor, note)
return note
}
/**
* Reblogs a note (representing a status)
*/
async function reblogNote(db: D1Database, reblogger: Person, noteToReblog: Note) {
await addObjectInOutbox(db, reblogger, noteToReblog)
await insertReblog(db, reblogger, noteToReblog)
}
/**
* Creates a reply for a note (representing a status)
*/
async function createReply(
domain: string,
db: D1Database,
reply: MastodonStatus,
loadedStatuses: { status: MastodonStatus; note: Note }[]
) {
if (!reply.in_reply_to_id) {
console.warn(`Ignoring reply with id ${reply.id} since it doesn't have a in_reply_to_id field`)
return
}
const headers = {
'content-type': 'application/json',
const originalStatus = loadedStatuses.find(({ status: { id } }) => id === reply.in_reply_to_id)
if (!originalStatus) {
console.warn(`Ignoring reply since no status matching the in_reply_to_id ${reply.id} has been found`)
return
}
const req = new Request('https://example.com', {
method: 'POST',
headers,
body: JSON.stringify(body),
})
const resp = await statusesAPI.handleRequest(req, db, actor, kek, queue, cache)
return (await resp.json()) as MastodonStatus
const inReplyTo = originalStatus.note.mastodonId
const actor = await getOrCreatePerson(domain, db, reply.account)
const replyNote = await createPublicNote(domain, db, reply.content, actor, [], { inReplyTo })
await insertReply(db, actor, replyNote, originalStatus.note)
}
async function getOrCreatePerson(
@ -61,7 +75,7 @@ async function getOrCreatePerson(
): Promise<Person> {
const person = await getPersonByEmail(db, username)
if (person) return person
const newPerson = await createPerson(domain, db, kek, username, {
const newPerson = await createPerson(domain, db, 'test-kek', username, {
icon: { url: avatar },
name: display_name,
})
@ -71,6 +85,20 @@ async function getOrCreatePerson(
return newPerson
}
async function reblogStatus(db: D1Database, actor: Person, status: MastodonStatus, domain: string) {
await reblogAPI.handleRequest(db, status.id, actor, kek, queue, domain)
/**
* Picks the details to use to reblog an arbitrary note/status.
*
* Both the note/status and the reblogger are picked arbitrarily
* form a list of available notes/states (respectively from the first
* and second entries).
*/
async function pickReblogDetails(
loadedStatuses: { status: MastodonStatus; note: Note }[],
domain: string,
db: D1Database
) {
const rebloggerAccount = loadedStatuses[1].status.account
const reblogger = await getOrCreatePerson(domain, db, rebloggerAccount)
const noteToReblog = loadedStatuses[2].note
return { reblogger, noteToReblog }
}

Wyświetl plik

@ -1557,6 +1557,122 @@ export const statuses: MastodonStatus[] = [
},
]
export const replies: MastodonStatus[] = [
{
id: '209630407170172986',
created_at: '2023-01-04T17:14:17.855Z',
in_reply_to_id: '109630407170172986',
in_reply_to_account_id: null,
sensitive: false,
spoiler_text: '',
visibility: 'public',
language: 'en',
uri: 'https://mastodon.social/users/ZachWeinersmith/statuses/209630407170172986',
url: 'https://mastodon.social/@ZachWeinersmith/209630407170172986',
replies_count: 52,
reblogs_count: 630,
favourites_count: 1434,
edited_at: '2023-01-04T10:15:59.795Z',
content: '\u003cp\u003e Yes we did! \u003c/p\u003e',
reblog: null,
application: { name: 'Web', website: null },
account: {
id: '109521032613939160',
username: 'ZachWeinersmith',
acct: 'ZachWeinersmith',
display_name: 'Zach Weinersmith',
locked: false,
bot: false,
discoverable: true,
group: false,
created_at: '2022-12-16T00:00:00.000Z',
note: '\u003cp\u003eThe SMBC guy. Co-author of Soonish, illustrator of Open Borders, scop of Bea Wolf.\u003c/p\u003e',
url: 'https://mastodon.social/@ZachWeinersmith',
avatar: 'https://files.mastodon.social/accounts/avatars/109/521/032/613/939/160/original/6d6588cad807dfbc.png',
avatar_static:
'https://files.mastodon.social/accounts/avatars/109/521/032/613/939/160/original/6d6588cad807dfbc.png',
header: 'https://files.mastodon.social/accounts/headers/109/521/032/613/939/160/original/8368e47ebdb7f00f.jpg',
header_static:
'https://files.mastodon.social/accounts/headers/109/521/032/613/939/160/original/8368e47ebdb7f00f.jpg',
followers_count: 4112,
following_count: 514,
statuses_count: 142,
last_status_at: '2023-01-04',
noindex: false,
emojis: [],
fields: [],
},
media_attachments: [],
mentions: [],
tags: [],
emojis: [],
card: null,
poll: null,
},
{
id: '309630407170172986',
created_at: '2023-01-04T22:14:17.855Z',
in_reply_to_id: '109630407170172986',
in_reply_to_account_id: null,
sensitive: false,
spoiler_text: '',
visibility: 'public',
language: 'en',
uri: 'https://mastodon.social/users/ZachWeinersmith/statuses/209630407170172986',
url: 'https://mastodon.social/@ZachWeinersmith/209630407170172986',
replies_count: 52,
reblogs_count: 630,
favourites_count: 1434,
edited_at: '2023-01-04T10:15:59.795Z',
content: '\u003cp\u003e Yes you guys did it! \u003c/p\u003e',
reblog: null,
application: { name: 'Web', website: null },
account: {
id: '38659',
username: 'nixCraft',
acct: 'nixCraft',
display_name: 'nixCraft 🐧',
locked: false,
bot: false,
discoverable: true,
group: false,
created_at: '2017-04-03T00:00:00.000Z',
note: '\u003cp\u003eEnjoy \u003ca href="https://mastodon.social/tags/Linux" class="mention hashtag" rel="tag"\u003e#\u003cspan\u003eLinux\u003c/span\u003e\u003c/a\u003e, \u003ca href="https://mastodon.social/tags/macOS" class="mention hashtag" rel="tag"\u003e#\u003cspan\u003emacOS\u003c/span\u003e\u003c/a\u003e, \u003ca href="https://mastodon.social/tags/FreeBSD" class="mention hashtag" rel="tag"\u003e#\u003cspan\u003eFreeBSD\u003c/span\u003e\u003c/a\u003e \u0026amp; \u003ca href="https://mastodon.social/tags/Unix" class="mention hashtag" rel="tag"\u003e#\u003cspan\u003eUnix\u003c/span\u003e\u003c/a\u003e like systems? \u003ca href="https://mastodon.social/tags/Opensource" class="mention hashtag" rel="tag"\u003e#\u003cspan\u003eOpensource\u003c/span\u003e\u003c/a\u003e software \u0026amp; \u003ca href="https://mastodon.social/tags/programming" class="mention hashtag" rel="tag"\u003e#\u003cspan\u003eprogramming\u003c/span\u003e\u003c/a\u003e? Enjoy \u003ca href="https://mastodon.social/tags/Sysadmin" class="mention hashtag" rel="tag"\u003e#\u003cspan\u003eSysadmin\u003c/span\u003e\u003c/a\u003e \u0026amp; \u003ca href="https://mastodon.social/tags/DevOps" class="mention hashtag" rel="tag"\u003e#\u003cspan\u003eDevOps\u003c/span\u003e\u003c/a\u003e work? Follow us to make the most of your geeky IT career.\u003c/p\u003e',
url: 'https://mastodon.social/@nixCraft',
avatar: 'https://files.mastodon.social/accounts/avatars/000/038/659/original/b0de8058da52f2aa.jpg',
avatar_static: 'https://files.mastodon.social/accounts/avatars/000/038/659/original/b0de8058da52f2aa.jpg',
header: 'https://files.mastodon.social/accounts/headers/000/038/659/original/6c4d08cfd6c28163.png',
header_static: 'https://files.mastodon.social/accounts/headers/000/038/659/original/6c4d08cfd6c28163.png',
followers_count: 14151,
following_count: 13,
statuses_count: 824,
last_status_at: '2023-01-04',
noindex: false,
emojis: [],
fields: [
{
name: 'Linux blog',
value:
'\u003ca href="https://www.cyberciti.biz" target="_blank" rel="nofollow noopener noreferrer me"\u003e\u003cspan class="invisible"\u003ehttps://www.\u003c/span\u003e\u003cspan class=""\u003ecyberciti.biz\u003c/span\u003e\u003cspan class="invisible"\u003e\u003c/span\u003e\u003c/a\u003e',
verified_at: '2022-11-21T11:10:53.864+00:00',
},
{
name: 'Q \u0026 A forum',
value:
'\u003ca href="https://www.nixcraft.com" target="_blank" rel="nofollow noopener noreferrer me"\u003e\u003cspan class="invisible"\u003ehttps://www.\u003c/span\u003e\u003cspan class=""\u003enixcraft.com\u003c/span\u003e\u003cspan class="invisible"\u003e\u003c/span\u003e\u003c/a\u003e',
verified_at: '2022-12-18T08:41:11.287+00:00',
},
],
},
media_attachments: [],
mentions: [],
tags: [],
emojis: [],
card: null,
poll: null,
},
]
export const tags: TagDetails[] = [
{
name: 'wintersolstice',

Wyświetl plik

@ -29,6 +29,10 @@ describe('Toot details', () => {
const response = await fetch(`http://0.0.0.0:6868/${tootPath}`)
expect(response.status).toBe(200)
const body = await response.text()
// validate the toot content itself
expect(body).toContain('We did it!')
// validate replies
expect(body).toContain('Yes we did!')
expect(body).toContain('Yes you guys did it!')
})
})