Lex refactor (#362)
* Remove the hackcheck for upgrades * Rename the PostEmbeds folder to match the codebase style * Updates to latest lex refactor * Update to use new bsky agent * Update to use api package's richtext library * Switch to upsertProfile * Add TextEncoder/TextDecoder polyfill * Add Intl.Segmenter polyfill * Update composer to calculate lengths by grapheme * Fix detox * Fix login in e2e * Create account e2e passing * Implement an e2e mocking framework * Don't use private methods on mobx models as mobx can't track them * Add tooling for e2e-specific builds and add e2e media-picker mock * Add some tests and fix some bugs around profile editing * Add shell tests * Add home screen tests * Add thread screen tests * Add tests for other user profile screens * Add search screen tests * Implement profile imagery change tools and tests * Update to new embed behaviors * Add post tests * Fix to profile-screen test * Fix session resumption * Update web composer to new api * 1.11.0 * Fix pagination cursor parameters * Add quote posts to notifications * Fix embed layouts * Remove youtube inline player and improve tap handling on link cards * Reset minimal shell mode on all screen loads and feed swipes (close #299) * Update podfile.lock * Improve post notfound UI (close #366) * Bump atproto packages
This commit is contained in:
parent
19f3a2fa92
commit
a3334a01a2
133 changed files with 3103 additions and 2839 deletions
257
jest/test-pds.ts
257
jest/test-pds.ts
|
@ -1,86 +1,73 @@
|
|||
import {AddressInfo} from 'net'
|
||||
import os from 'os'
|
||||
import net from 'net'
|
||||
import path from 'path'
|
||||
import * as crypto from '@atproto/crypto'
|
||||
import PDSServer, {
|
||||
Database as PDSDatabase,
|
||||
MemoryBlobStore,
|
||||
ServerConfig as PDSServerConfig,
|
||||
} from '@atproto/pds'
|
||||
import * as plc from '@atproto/plc'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import {PDS, ServerConfig, Database, MemoryBlobStore} from '@atproto/pds'
|
||||
import * as plc from '@did-plc/lib'
|
||||
import {PlcServer, Database as PlcDatabase} from '@did-plc/server'
|
||||
import {BskyAgent} from '@atproto/api'
|
||||
|
||||
const ADMIN_PASSWORD = 'admin-pass'
|
||||
const SECOND = 1000
|
||||
const MINUTE = SECOND * 60
|
||||
const HOUR = MINUTE * 60
|
||||
|
||||
export interface TestUser {
|
||||
email: string
|
||||
did: string
|
||||
declarationCid: string
|
||||
handle: string
|
||||
password: string
|
||||
agent: AtpAgent
|
||||
}
|
||||
|
||||
export interface TestUsers {
|
||||
alice: TestUser
|
||||
bob: TestUser
|
||||
carla: TestUser
|
||||
agent: BskyAgent
|
||||
}
|
||||
|
||||
export interface TestPDS {
|
||||
pdsUrl: string
|
||||
users: TestUsers
|
||||
mocker: Mocker
|
||||
close: () => Promise<void>
|
||||
}
|
||||
|
||||
// NOTE
|
||||
// deterministic date generator
|
||||
// we use this to ensure the mock dataset is always the same
|
||||
// which is very useful when testing
|
||||
function* dateGen() {
|
||||
let start = 1657846031914
|
||||
while (true) {
|
||||
yield new Date(start).toISOString()
|
||||
start += 1e3
|
||||
}
|
||||
}
|
||||
|
||||
export async function createServer(): Promise<TestPDS> {
|
||||
const keypair = await crypto.EcdsaKeypair.create()
|
||||
const repoSigningKey = await crypto.Secp256k1Keypair.create()
|
||||
const plcRotationKey = await crypto.Secp256k1Keypair.create()
|
||||
const port = await getPort()
|
||||
|
||||
// run plc server
|
||||
const plcDb = plc.Database.memory()
|
||||
await plcDb.migrateToLatestOrThrow()
|
||||
const plcServer = plc.PlcServer.create({db: plcDb})
|
||||
const plcDb = PlcDatabase.mock()
|
||||
|
||||
const plcServer = PlcServer.create({db: plcDb})
|
||||
const plcListener = await plcServer.start()
|
||||
const plcPort = (plcListener.address() as AddressInfo).port
|
||||
const plcUrl = `http://localhost:${plcPort}`
|
||||
|
||||
const recoveryKey = (await crypto.EcdsaKeypair.create()).did()
|
||||
const recoveryKey = (await crypto.Secp256k1Keypair.create()).did()
|
||||
|
||||
const plcClient = new plc.PlcClient(plcUrl)
|
||||
const serverDid = await plcClient.createDid(
|
||||
keypair,
|
||||
recoveryKey,
|
||||
'localhost',
|
||||
'https://pds.public.url',
|
||||
)
|
||||
const plcClient = new plc.Client(plcUrl)
|
||||
const serverDid = await plcClient.createDid({
|
||||
signingKey: repoSigningKey.did(),
|
||||
rotationKeys: [recoveryKey, plcRotationKey.did()],
|
||||
handle: 'localhost',
|
||||
pds: `http://localhost:${port}`,
|
||||
signer: plcRotationKey,
|
||||
})
|
||||
|
||||
const blobstoreLoc = path.join(os.tmpdir(), crypto.randomStr(5, 'base32'))
|
||||
|
||||
const cfg = new PDSServerConfig({
|
||||
const cfg = new ServerConfig({
|
||||
debugMode: true,
|
||||
version: '0.0.0',
|
||||
scheme: 'http',
|
||||
hostname: 'localhost',
|
||||
port,
|
||||
serverDid,
|
||||
recoveryKey,
|
||||
adminPassword: 'admin-pass',
|
||||
adminPassword: ADMIN_PASSWORD,
|
||||
inviteRequired: false,
|
||||
didPlcUrl: plcUrl,
|
||||
jwtSecret: 'jwt-secret',
|
||||
availableUserDomains: ['.test'],
|
||||
appUrlPasswordReset: 'app://forgot-password',
|
||||
emailNoReplyAddress: 'noreply@blueskyweb.xyz',
|
||||
publicUrl: 'https://pds.public.url',
|
||||
publicUrl: `http://localhost:${port}`,
|
||||
imgUriSalt: '9dd04221f5755bce5f55f47464c27e1e',
|
||||
imgUriKey:
|
||||
'f23ecd142835025f42c3db2cf25dd813956c178392760256211f9d315f8ab4d8',
|
||||
|
@ -88,22 +75,33 @@ export async function createServer(): Promise<TestPDS> {
|
|||
blobstoreLocation: `${blobstoreLoc}/blobs`,
|
||||
blobstoreTmp: `${blobstoreLoc}/tmp`,
|
||||
maxSubscriptionBuffer: 200,
|
||||
repoBackfillLimitMs: 1e3 * 60 * 60,
|
||||
repoBackfillLimitMs: HOUR,
|
||||
})
|
||||
|
||||
const db = PDSDatabase.memory()
|
||||
const db =
|
||||
cfg.dbPostgresUrl !== undefined
|
||||
? Database.postgres({
|
||||
url: cfg.dbPostgresUrl,
|
||||
schema: cfg.dbPostgresSchema,
|
||||
})
|
||||
: Database.memory()
|
||||
await db.migrateToLatestOrThrow()
|
||||
|
||||
const blobstore = new MemoryBlobStore()
|
||||
|
||||
const pds = PDSServer.create({db, blobstore, keypair, config: cfg})
|
||||
const pdsServer = await pds.start()
|
||||
const pdsPort = (pdsServer.address() as AddressInfo).port
|
||||
const pdsUrl = `http://localhost:${pdsPort}`
|
||||
const testUsers = await genMockData(pdsUrl)
|
||||
const pds = PDS.create({
|
||||
db,
|
||||
blobstore,
|
||||
repoSigningKey,
|
||||
plcRotationKey,
|
||||
config: cfg,
|
||||
})
|
||||
await pds.start()
|
||||
const pdsUrl = `http://localhost:${port}`
|
||||
|
||||
return {
|
||||
pdsUrl,
|
||||
users: testUsers,
|
||||
mocker: new Mocker(pdsUrl),
|
||||
async close() {
|
||||
await pds.destroy()
|
||||
await plcServer.destroy()
|
||||
|
@ -111,90 +109,93 @@ export async function createServer(): Promise<TestPDS> {
|
|||
}
|
||||
}
|
||||
|
||||
async function genMockData(pdsUrl: string): Promise<TestUsers> {
|
||||
const date = dateGen()
|
||||
class Mocker {
|
||||
agent: BskyAgent
|
||||
users: Record<string, TestUser> = {}
|
||||
|
||||
const agents = {
|
||||
loggedout: new AtpAgent({service: pdsUrl}),
|
||||
alice: new AtpAgent({service: pdsUrl}),
|
||||
bob: new AtpAgent({service: pdsUrl}),
|
||||
carla: new AtpAgent({service: pdsUrl}),
|
||||
constructor(public service: string) {
|
||||
this.agent = new BskyAgent({service})
|
||||
}
|
||||
const users: TestUser[] = [
|
||||
{
|
||||
email: 'alice@test.com',
|
||||
did: '',
|
||||
declarationCid: '',
|
||||
handle: 'alice.test',
|
||||
password: 'hunter2',
|
||||
agent: agents.alice,
|
||||
},
|
||||
{
|
||||
email: 'bob@test.com',
|
||||
did: '',
|
||||
declarationCid: '',
|
||||
handle: 'bob.test',
|
||||
password: 'hunter2',
|
||||
agent: agents.bob,
|
||||
},
|
||||
{
|
||||
email: 'carla@test.com',
|
||||
did: '',
|
||||
declarationCid: '',
|
||||
handle: 'carla.test',
|
||||
password: 'hunter2',
|
||||
agent: agents.carla,
|
||||
},
|
||||
]
|
||||
const alice = users[0]
|
||||
const bob = users[1]
|
||||
const carla = users[2]
|
||||
|
||||
let _i = 1
|
||||
for (const user of users) {
|
||||
const res = await agents.loggedout.api.com.atproto.account.create({
|
||||
email: user.email,
|
||||
handle: user.handle,
|
||||
password: user.password,
|
||||
// NOTE
|
||||
// deterministic date generator
|
||||
// we use this to ensure the mock dataset is always the same
|
||||
// which is very useful when testing
|
||||
*dateGen() {
|
||||
let start = 1657846031914
|
||||
while (true) {
|
||||
yield new Date(start).toISOString()
|
||||
start += 1e3
|
||||
}
|
||||
}
|
||||
|
||||
async createUser(name: string) {
|
||||
const agent = new BskyAgent({service: this.agent.service})
|
||||
const email = `fake${Object.keys(this.users).length + 1}@fake.com`
|
||||
const res = await agent.createAccount({
|
||||
email,
|
||||
handle: name + '.test',
|
||||
password: 'hunter2',
|
||||
})
|
||||
user.agent.api.setHeader('Authorization', `Bearer ${res.data.accessJwt}`)
|
||||
const {data: profile} = await user.agent.api.app.bsky.actor.getProfile({
|
||||
actor: user.handle,
|
||||
})
|
||||
user.did = res.data.did
|
||||
user.declarationCid = profile.declaration.cid
|
||||
await user.agent.api.app.bsky.actor.profile.create(
|
||||
{did: user.did},
|
||||
{
|
||||
displayName: ucfirst(user.handle).slice(0, -5),
|
||||
description: `Test user ${_i++}`,
|
||||
},
|
||||
)
|
||||
this.users[name] = {
|
||||
did: res.data.did,
|
||||
email,
|
||||
handle: name + '.test',
|
||||
password: 'hunter2',
|
||||
agent: agent,
|
||||
}
|
||||
}
|
||||
|
||||
// everybody follows everybody
|
||||
const follow = async (author: TestUser, subject: TestUser) => {
|
||||
await author.agent.api.app.bsky.graph.follow.create(
|
||||
{did: author.did},
|
||||
{
|
||||
subject: {
|
||||
did: subject.did,
|
||||
declarationCid: subject.declarationCid,
|
||||
},
|
||||
createdAt: date.next().value || '',
|
||||
},
|
||||
)
|
||||
async follow(a: string, b: string) {
|
||||
await this.users[a].agent.follow(this.users[b].did)
|
||||
}
|
||||
await follow(alice, bob)
|
||||
await follow(alice, carla)
|
||||
await follow(bob, alice)
|
||||
await follow(bob, carla)
|
||||
await follow(carla, alice)
|
||||
await follow(carla, bob)
|
||||
|
||||
return {alice, bob, carla}
|
||||
async generateStandardGraph() {
|
||||
await this.createUser('alice')
|
||||
await this.createUser('bob')
|
||||
await this.createUser('carla')
|
||||
|
||||
await this.users.alice.agent.upsertProfile(() => ({
|
||||
displayName: 'Alice',
|
||||
description: 'Test user 1',
|
||||
}))
|
||||
|
||||
await this.users.bob.agent.upsertProfile(() => ({
|
||||
displayName: 'Bob',
|
||||
description: 'Test user 2',
|
||||
}))
|
||||
|
||||
await this.users.carla.agent.upsertProfile(() => ({
|
||||
displayName: 'Carla',
|
||||
description: 'Test user 3',
|
||||
}))
|
||||
|
||||
await this.follow('alice', 'bob')
|
||||
await this.follow('alice', 'carla')
|
||||
await this.follow('bob', 'alice')
|
||||
await this.follow('bob', 'carla')
|
||||
await this.follow('carla', 'alice')
|
||||
await this.follow('carla', 'bob')
|
||||
}
|
||||
}
|
||||
|
||||
function ucfirst(str: string): string {
|
||||
return str.at(0)?.toUpperCase() + str.slice(1)
|
||||
const checkAvailablePort = (port: number) =>
|
||||
new Promise(resolve => {
|
||||
const server = net.createServer()
|
||||
server.unref()
|
||||
server.on('error', () => resolve(false))
|
||||
server.listen({port}, () => {
|
||||
server.close(() => {
|
||||
resolve(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
async function getPort() {
|
||||
for (let i = 3000; i < 65000; i++) {
|
||||
if (await checkAvailablePort(i)) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
throw new Error('Unable to find an available port')
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue