kopia lustrzana https://github.com/cloudflare/wildebeest
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
rodzic
eea4599add
commit
e464fe2bcd
|
@ -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',
|
||||
},
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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!')
|
||||
})
|
||||
})
|
||||
|
|
Ładowanie…
Reference in New Issue