about summary refs log tree commit diff
path: root/jest
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2023-03-31 13:17:26 -0500
committerGitHub <noreply@github.com>2023-03-31 13:17:26 -0500
commita3334a01a221877d3e06e02f960fda441f3460bd (patch)
tree64cdbb1232d1a3c00750c346b6e3ae529b51d1b0 /jest
parent19f3a2fa92a61ddb785fc4e42d73792c1d0e772c (diff)
downloadvoidsky-a3334a01a221877d3e06e02f960fda441f3460bd.tar.zst
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
Diffstat (limited to 'jest')
-rw-r--r--jest/test-pds.ts259
1 files changed, 130 insertions, 129 deletions
diff --git a/jest/test-pds.ts b/jest/test-pds.ts
index 32f3bc9b0..1e87df811 100644
--- a/jest/test-pds.ts
+++ b/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()
+
+  const plcDb = PlcDatabase.mock()
 
-  // run plc server
-  const plcDb = plc.Database.memory()
-  await plcDb.migrateToLatestOrThrow()
-  const plcServer = plc.PlcServer.create({db: plcDb})
+  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',
+
+  // 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',
-      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,
-    })
-    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)
+  }
+
+  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')
   }
-  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}
 }
 
-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')
 }