b934b396b1
* feat(api): support creation of oauth based AtpAgents * oauth: misc fixes for confidential clients * fix(xprc): remove ReadableStream.from polyfill * OAuth docs tweaks (#2679) * OAuth: clarification about client_name being shown * OAuth: re-write handle resolution privacy concern * avoid relying on ReadableStream.from in xrpc-server tests * feat(oauth-types): expose "ALLOW_UNSECURE_ORIGINS" constant * feat(handle-resolver): expose "AtprotoIdentityDidMethods" type * fix(oauth-client): ensure that the oauth metadata document contains client_id_metadata_document_supported * fix(oauth-types): prevent unknown query string in loopback client id * fix(identity-resolver): check that handle is in did doc's "alsoKnownAs" * feat(oauth-client:oauth-resolver): allow logging in using either the PDS URL or Entryway URL * fix(oauth-client): return better error in case of invalid "oauth-protected-resource" status code * refactor(did): group atproto specific checks in own * feat(api): relax typing of "appLabelers" and "labelers" AtpClient properties * allow any did as labeller (for tests mainly) * fix(api): allow to override "atproto-proxy" on a per-request basis * remove release candidate versions from changelog * update changeset for api and xrpc packages * Add missing changeset * revert RC versions * Proper wording in OAUTH.md api example * remove "pre" changeset file * xrpc: restore original behavior of setHEader and unsetHeader * docs: add comment for XrpcClient 's constructor arg * feat(api): expose "schemas" publicly * feat(api): allow customizing the whatwg fetch function of the AtpAgent * docs(api): improve migration docs * docs: change reference to BskyAgent to AtpAgent * docs: mention the breaking change regarding setSessionPersistHandler * fix(api): better split AtpClient concerns * fix(xrpc): remove unused import * refactor(api): simplify class hierarchu by removeing AtpClient * fix(api): mock proper method for facets detection * restore ability to restore session asynchronously * feat(api): allow instantiating Agent with same argument as super class * docs(api): properly extend Agent class * style(xrpc): var name * docs(api): remove "async" to header getter --------- Co-authored-by: Devin Ivy <devinivy@gmail.com> Co-authored-by: bnewbold <bnewbold@robocracy.org> Co-authored-by: Hailey <me@haileyok.com>
164 lines
5.6 KiB
TypeScript
164 lines
5.6 KiB
TypeScript
import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
|
|
import { AtpAgent } from '@atproto/api'
|
|
import { forSnapshot } from './_util'
|
|
|
|
describe('team management', () => {
|
|
let network: TestNetwork
|
|
let adminAgent: AtpAgent
|
|
let triageAgent: AtpAgent
|
|
let sc: SeedClient
|
|
|
|
beforeAll(async () => {
|
|
network = await TestNetwork.create({
|
|
dbPostgresSchema: 'ozone_team_test',
|
|
})
|
|
adminAgent = network.pds.getClient()
|
|
sc = network.getSeedClient()
|
|
await basicSeed(sc)
|
|
await network.processAll()
|
|
|
|
await network.ozone.addAdminDid(sc.dids.alice)
|
|
await network.ozone.addModeratorDid(sc.dids.bob)
|
|
await network.ozone.addTriageDid(sc.dids.carol)
|
|
await adminAgent.login({
|
|
identifier: sc.accounts[sc.dids.alice].handle,
|
|
password: sc.accounts[sc.dids.alice].password,
|
|
})
|
|
triageAgent = network.pds.getClient()
|
|
await triageAgent.login({
|
|
identifier: sc.accounts[sc.dids.carol].handle,
|
|
password: sc.accounts[sc.dids.carol].password,
|
|
})
|
|
})
|
|
|
|
afterAll(async () => {
|
|
await network.close()
|
|
})
|
|
|
|
describe('listMembers', () => {
|
|
it('allows all members to list all members', async () => {
|
|
const [{ data: forAdmin }, { data: forTriage }] = await Promise.all([
|
|
adminAgent.api.tools.ozone.team.listMembers({}),
|
|
triageAgent.api.tools.ozone.team.listMembers({}),
|
|
])
|
|
expect(forSnapshot(forAdmin.members)).toMatchSnapshot()
|
|
expect(forSnapshot(forTriage.members)).toMatchSnapshot()
|
|
// Validate that the list looks the same to both admin and triage members
|
|
|
|
expect(forAdmin.members.length).toEqual(forTriage.members.length)
|
|
})
|
|
})
|
|
describe('listMembers', () => {
|
|
it('allows all members to list all members', async () => {
|
|
const [{ data: forAdmin }, { data: forTriage }] = await Promise.all([
|
|
adminAgent.api.tools.ozone.team.listMembers({}),
|
|
triageAgent.api.tools.ozone.team.listMembers({}),
|
|
])
|
|
expect(forSnapshot(forAdmin.members)).toMatchSnapshot()
|
|
expect(forSnapshot(forTriage.members)).toMatchSnapshot()
|
|
// Validate that the list looks the same to both admin and triage members
|
|
|
|
expect(forAdmin.members.length).toEqual(forTriage.members.length)
|
|
})
|
|
})
|
|
describe('addMember', () => {
|
|
const newMemberData = {
|
|
did: 'did:plc:newMember',
|
|
role: 'tools.ozone.team.defs#roleAdmin',
|
|
disabled: false,
|
|
}
|
|
it('only allows admins to add member', async () => {
|
|
await expect(
|
|
triageAgent.api.tools.ozone.team.addMember(newMemberData),
|
|
).rejects.toThrow('Must be an admin to add a member')
|
|
const { data: newMember } =
|
|
await adminAgent.api.tools.ozone.team.addMember(newMemberData)
|
|
expect(forSnapshot(newMember)).toMatchSnapshot()
|
|
})
|
|
it('throws error when trying to add existing member', async () => {
|
|
await expect(
|
|
adminAgent.api.tools.ozone.team.addMember(newMemberData),
|
|
).rejects.toThrow('member already exists')
|
|
})
|
|
})
|
|
describe('deleteMember', () => {
|
|
it('only allows admins to delete members', async () => {
|
|
const {
|
|
data: { members: initialMembers },
|
|
} = await adminAgent.api.tools.ozone.team.listMembers({})
|
|
await expect(
|
|
triageAgent.api.tools.ozone.team.deleteMember({
|
|
did: sc.dids.bob,
|
|
}),
|
|
).rejects.toThrow('Must be an admin to delete a member')
|
|
|
|
await adminAgent.api.tools.ozone.team.deleteMember({
|
|
did: sc.dids.bob,
|
|
})
|
|
const {
|
|
data: { members: membersAfterDelete },
|
|
} = await adminAgent.api.tools.ozone.team.listMembers({})
|
|
|
|
expect(membersAfterDelete.length).toEqual(initialMembers.length - 1)
|
|
expect(membersAfterDelete.map(({ did }) => did)).not.toContain(
|
|
sc.dids.bob,
|
|
)
|
|
})
|
|
|
|
it('throws error when trying to remove non-existent member', async () => {
|
|
await expect(
|
|
adminAgent.api.tools.ozone.team.deleteMember({
|
|
did: 'did:plc:test',
|
|
}),
|
|
).rejects.toThrow('member not found')
|
|
})
|
|
})
|
|
describe('updateMember', () => {
|
|
it('allows admins to update member', async () => {
|
|
const getCarol = async () => {
|
|
const {
|
|
data: { members },
|
|
} = await adminAgent.api.tools.ozone.team.listMembers({})
|
|
|
|
return members.find(({ did }) => did === sc.dids.carol)
|
|
}
|
|
await expect(
|
|
triageAgent.api.tools.ozone.team.updateMember({
|
|
disabled: false,
|
|
did: sc.dids.carol,
|
|
role: 'tools.ozone.team.defs#roleAdmin',
|
|
}),
|
|
).rejects.toThrow('Must be an admin to update a member')
|
|
|
|
await adminAgent.api.tools.ozone.team.updateMember({
|
|
did: sc.dids.carol,
|
|
role: 'tools.ozone.team.defs#roleAdmin',
|
|
})
|
|
const carolAfterRoleChange = await getCarol()
|
|
expect(carolAfterRoleChange?.role).toEqual(
|
|
'tools.ozone.team.defs#roleAdmin',
|
|
)
|
|
// Verify that params that we didn't send did not get updated
|
|
expect(carolAfterRoleChange?.disabled).toEqual(false)
|
|
|
|
await adminAgent.api.tools.ozone.team.updateMember({
|
|
did: sc.dids.carol,
|
|
disabled: true,
|
|
})
|
|
const carolAfterDisable = await getCarol()
|
|
expect(carolAfterDisable?.disabled).toEqual(true)
|
|
// Verify that params that we didn't send did not get updated
|
|
expect(carolAfterDisable?.role).toEqual('tools.ozone.team.defs#roleAdmin')
|
|
})
|
|
it('throws error when trying to update non-existent member', async () => {
|
|
await expect(
|
|
adminAgent.api.tools.ozone.team.updateMember({
|
|
disabled: false,
|
|
did: 'did:plc:test',
|
|
role: 'tools.ozone.team.defs#roleAdmin',
|
|
}),
|
|
).rejects.toThrow('member not found')
|
|
})
|
|
})
|
|
})
|