// gip-overlay P4 — admin revoke of repo push authority over RPC.
//
// Grant happens via create-repo-invite/join-repo (addWriter during pairing).
// This covers the revoke half: an admin removes a writer from a repo's
// autobase via repo-revoke-writer, after which that writer is out of the
// push-authority set. repo-list-writers exposes the set. (The authority
// SEMANTICS — forged/post-revoke ops not applied — are proven in
// hyper-issues basic.test.js "refs authority".)
import test from 'brittle'
import { mkdtempSync } from '#node:fs'
import { join } from '#node:path'
import { tmpdir } from '#node:os'
import { Client } from 'hyper-svc'
import { startPearGit } from '../index.js'
function tmpStorage () { return mkdtempSync(join(tmpdir(), 'pgv3-acl-')) }
function tmpSock () { return join(mkdtempSync(join(tmpdir(), 'pgv3-acl-sock-')), 'svc.sock') }
test('admin revokes a repo writer via RPC; writer-set reflects it', async (t) => {
const node = await startPearGit({ storage: tmpStorage(), uds: tmpSock(), http: { port: 0 }, swarm: false, autoImportSSH: false })
t.teardown(async () => { try { await node.close() } catch {} })
// grant: stand in for join-repo by adding a writer directly
const tracker = await node.svc.issueRegistry.getForRepo('gip-acl')
const bobKey = '1'.padStart(64, 'b')
await tracker.addWriter('bob', { writerKey: bobKey, role: 'writer' })
const client = await Client.discover({ uds: node.svc.uds.path })
t.teardown(async () => { try { await client.close() } catch {} })
const before = await client.call('repo-list-writers', { name: 'gip-acl' })
t.ok(before.writers.some((w) => w.userId === 'bob'), 'bob is in the push-authority set')
const rev = await client.call('repo-revoke-writer', { name: 'gip-acl', userId: 'bob' })
t.ok(rev && rev.revoked, 'repo-revoke-writer succeeded')
const after = await client.call('repo-list-writers', { name: 'gip-acl' })
t.absent(after.writers.some((w) => w.userId === 'bob'), 'bob removed from the writer set')
})