atproto/packages/pds/tests/account-migration.test.ts
Daniel Holmgren 30b05a7d4b
Account migration ()
* draft of account migration lexicons

* format

* clean up schemas

* codegen

* second pass on schemas

* small fix

* move around checkImportStatus

* re-codegen

* getServiceAuth

* getServiceAuth impl

* importRepo impl

* handle uploadBlob for import

* allow bringing your own did on createAccount

* working on test flow

* fleshing out flow

* fix up sendPlcOP

* small fixes

* activate/deactivate account

* full flow working!

* schema tweaks

* format

* update schemas

* moar codegen

* match impl to new schemas

* email flow for signed plc operation

* add email flow for plc operations

* impl plc op request email

* proxy to entryway

* tidy activate account

* integrating account deactivated state

* fix up tests

* friendly parse on optional did auth

* admin activate/deactivate routes

* proxy relevant requests to entryway

* remove admin activation routes

* do not proxy acitvaition to entryway

* cfg for disallowing imports

* buff up test

* refactor listMissingBlobs a bit

* add validDid & activated to accoutn status

* emit event on account activation

* test creating a post after migrating

* account deactivation tests

* test name

* tests on plc operations

* fix recommended did creds

* codegen

* turn off accepting imports on createAccount

* undo prev change

* increment version

* build branch

* pr feedback

* handle errs in p-queue

* handle blob upload outside of txn

* Clean old temp account migration lexicons ()

* clean old temp lexicons

* rm old test

* fix agent session test

* fix bsky test

* dont build branch
2024-02-20 19:29:49 -06:00

220 lines
6.1 KiB
TypeScript

import AtpAgent, { AtUri } from '@atproto/api'
import {
SeedClient,
TestNetworkNoAppView,
TestPds,
mockNetworkUtilities,
} from '@atproto/dev-env'
import { readCar } from '@atproto/repo'
import assert from 'assert'
describe('account migration', () => {
let network: TestNetworkNoAppView
let newPds: TestPds
let sc: SeedClient
let oldAgent: AtpAgent
let newAgent: AtpAgent
let alice: string
beforeAll(async () => {
network = await TestNetworkNoAppView.create({
dbPostgresSchema: 'account_migration',
})
newPds = await TestPds.create({
didPlcUrl: network.plc.url,
})
mockNetworkUtilities(newPds)
sc = network.getSeedClient()
oldAgent = network.pds.getClient()
newAgent = newPds.getClient()
await sc.createAccount('alice', {
email: 'alice@test.com',
handle: 'alice.test',
password: 'alice-pass',
})
alice = sc.dids.alice
for (let i = 0; i < 100; i++) {
await sc.post(alice, 'test post')
}
const img1 = await sc.uploadFile(
alice,
'../dev-env/src/seed/img/at.png',
'image/png',
)
const img2 = await sc.uploadFile(
alice,
'../dev-env/src/seed/img/key-alt.jpg',
'image/jpeg',
)
const img3 = await sc.uploadFile(
alice,
'../dev-env/src/seed/img/key-landscape-small.jpg',
'image/jpeg',
)
await sc.post(alice, 'test', undefined, [img1])
await sc.post(alice, 'test', undefined, [img1, img2])
await sc.post(alice, 'test', undefined, [img3])
await network.processAll()
await oldAgent.login({
identifier: sc.accounts[alice].handle,
password: sc.accounts[alice].password,
})
})
afterAll(async () => {
await newPds.close()
await network.close()
})
it('migrates an account', async () => {
const describeRes = await newAgent.api.com.atproto.server.describeServer()
const newServerDid = describeRes.data.did
const serviceJwtRes = await oldAgent.com.atproto.server.getServiceAuth({
aud: newServerDid,
})
const serviceJwt = serviceJwtRes.data.token
await newAgent.api.com.atproto.server.createAccount(
{
handle: 'new-alice.test',
email: 'alice@test.com',
password: 'alice-pass',
did: alice,
},
{
headers: { authorization: `Bearer ${serviceJwt}` },
encoding: 'application/json',
},
)
await newAgent.login({
identifier: 'new-alice.test',
password: 'alice-pass',
})
const statusRes1 = await newAgent.com.atproto.server.checkAccountStatus()
expect(statusRes1.data).toMatchObject({
activated: false,
validDid: false,
repoBlocks: 2, // commit & empty data root
indexedRecords: 0,
privateStateValues: 0,
expectedBlobs: 0,
importedBlobs: 0,
})
const repoRes = await oldAgent.com.atproto.sync.getRepo({ did: alice })
const carBlocks = await readCar(repoRes.data)
await newAgent.com.atproto.repo.importRepo(repoRes.data, {
encoding: 'application/vnd.ipld.car',
})
const statusRes2 = await newAgent.com.atproto.server.checkAccountStatus()
expect(statusRes2.data).toMatchObject({
activated: false,
validDid: false,
indexedRecords: 103,
privateStateValues: 0,
expectedBlobs: 3,
importedBlobs: 0,
})
expect(statusRes2.data.repoBlocks).toBe(carBlocks.blocks.size)
const missingBlobs = await newAgent.com.atproto.repo.listMissingBlobs()
expect(missingBlobs.data.blobs.length).toBe(3)
let blobCursor: string | undefined = undefined
do {
const listedBlobs = await oldAgent.com.atproto.sync.listBlobs({
did: alice,
cursor: blobCursor,
})
for (const cid of listedBlobs.data.cids) {
const blobRes = await oldAgent.com.atproto.sync.getBlob({
did: alice,
cid,
})
await newAgent.com.atproto.repo.uploadBlob(blobRes.data, {
encoding: blobRes.headers['content-type'],
})
}
blobCursor = listedBlobs.data.cursor
} while (blobCursor)
const statusRes3 = await newAgent.com.atproto.server.checkAccountStatus()
expect(statusRes3.data.expectedBlobs).toBe(3)
expect(statusRes3.data.importedBlobs).toBe(3)
const prefs = await oldAgent.api.app.bsky.actor.getPreferences()
await newAgent.api.app.bsky.actor.putPreferences(prefs.data)
const getDidCredentials =
await newAgent.com.atproto.identity.getRecommendedDidCredentials()
await oldAgent.com.atproto.identity.requestPlcOperationSignature()
const res = await network.pds.ctx.accountManager.db.db
.selectFrom('email_token')
.selectAll()
.where('did', '=', alice)
.where('purpose', '=', 'plc_operation')
.executeTakeFirst()
const token = res?.token
assert(token)
const plcOp = await oldAgent.com.atproto.identity.signPlcOperation({
token,
...getDidCredentials.data,
})
await newAgent.com.atproto.identity.submitPlcOperation({
operation: plcOp.data.operation,
})
await newAgent.com.atproto.server.activateAccount()
const statusRes4 = await newAgent.com.atproto.server.checkAccountStatus()
expect(statusRes4.data).toMatchObject({
activated: true,
validDid: true,
indexedRecords: 103,
privateStateValues: 0,
expectedBlobs: 3,
importedBlobs: 3,
})
await oldAgent.com.atproto.server.deactivateAccount({})
const statusResOldPds =
await oldAgent.com.atproto.server.checkAccountStatus()
expect(statusResOldPds.data).toMatchObject({
activated: false,
validDid: false,
})
const postRes = await newAgent.api.app.bsky.feed.post.create(
{ repo: alice },
{
text: 'new pds!',
createdAt: new Date().toISOString(),
},
)
const postUri = new AtUri(postRes.uri)
const fetchedPost = await newAgent.api.app.bsky.feed.post.get({
repo: postUri.hostname,
rkey: postUri.rkey,
})
expect(fetchedPost.value.text).toEqual('new pds!')
const statusRes5 = await newAgent.com.atproto.server.checkAccountStatus()
expect(statusRes5.data.indexedRecords).toBe(104)
})
})