import test from 'brittle'
import {
encodeOp, decodeOp, validateOp,
OP_CREATE_USER, OP_UPDATE_USER,
OP_CREATE_IDENTITY, OP_LINK_IDENTITY, OP_UNLINK_IDENTITY,
OP_REVOKE_IDENTITY, OP_RESTORE_IDENTITY,
OP_UPGRADE_PERMISSIONS, OP_MERGE_ACCOUNTS
} from '../../lib/auth/identity-operations.js'
// ========================================
// Encode / Decode
// ========================================
test('encode returns a Buffer', async (t) => {
const op = { type: OP_CREATE_USER, userId: 'u1', timestamp: Date.now() }
const buf = encodeOp(op)
t.ok(Buffer.isBuffer(buf), 'encode returns Buffer')
})
test('encode/decode roundtrip preserves all fields', async (t) => {
const op = {
type: OP_CREATE_IDENTITY,
identityId: 'id1',
method: 'ssh-key',
timestamp: 1234567890,
extra: 'data'
}
const decoded = decodeOp(encodeOp(op))
t.is(decoded.type, op.type)
t.is(decoded.identityId, op.identityId)
t.is(decoded.method, op.method)
t.is(decoded.timestamp, op.timestamp)
t.is(decoded.extra, op.extra)
})
// ========================================
// Validation — valid operations
// ========================================
test('validate: create-user accepts valid op', async (t) => {
const res = validateOp({ type: OP_CREATE_USER, userId: 'u1', timestamp: 1 })
t.is(res.valid, true)
})
test('validate: update-user accepts valid op', async (t) => {
const res = validateOp({ type: OP_UPDATE_USER, userId: 'u1', timestamp: 1 })
t.is(res.valid, true)
})
test('validate: create-identity accepts all valid methods', async (t) => {
for (const method of ['web-identity', 'ssh-key', 'keet-identity', 'noise-key']) {
const res = validateOp({ type: OP_CREATE_IDENTITY, identityId: 'id1', method, timestamp: 1 })
t.is(res.valid, true, `method ${method} accepted`)
}
})
test('validate: link-identity accepts valid op', async (t) => {
const res = validateOp({ type: OP_LINK_IDENTITY, identityId: 'id1', userId: 'u1', timestamp: 1 })
t.is(res.valid, true)
})
test('validate: unlink-identity accepts valid op', async (t) => {
const res = validateOp({ type: OP_UNLINK_IDENTITY, identityId: 'id1', timestamp: 1 })
t.is(res.valid, true)
})
test('validate: revoke-identity accepts valid op', async (t) => {
const res = validateOp({ type: OP_REVOKE_IDENTITY, identityId: 'id1', timestamp: 1 })
t.is(res.valid, true)
})
test('validate: restore-identity accepts valid op', async (t) => {
const res = validateOp({ type: OP_RESTORE_IDENTITY, identityId: 'id1', timestamp: 1 })
t.is(res.valid, true)
})
test('validate: upgrade-permissions accepts valid op', async (t) => {
const res = validateOp({
type: OP_UPGRADE_PERMISSIONS,
userId: 'u1',
repoName: 'repo1',
permissions: ['write', 'admin'],
timestamp: 1
})
t.is(res.valid, true)
})
test('validate: merge-accounts accepts valid op', async (t) => {
const res = validateOp({
type: OP_MERGE_ACCOUNTS,
primaryUserId: 'u1',
secondaryUserId: 'u2',
timestamp: 1
})
t.is(res.valid, true)
})
// ========================================
// Validation — missing fields
// ========================================
test('validate: rejects missing type', async (t) => {
t.is(validateOp({}).valid, false)
t.is(validateOp(null).valid, false)
})
test('validate: rejects unknown type', async (t) => {
const res = validateOp({ type: 'unknown-op', timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('Unknown operation type'))
})
test('validate: rejects missing timestamp', async (t) => {
const res = validateOp({ type: OP_CREATE_USER, userId: 'u1' })
t.is(res.valid, false)
t.ok(res.error.includes('timestamp'))
})
test('validate: create-user rejects missing userId', async (t) => {
const res = validateOp({ type: OP_CREATE_USER, timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('userId'))
})
test('validate: update-user rejects missing userId', async (t) => {
const res = validateOp({ type: OP_UPDATE_USER, timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('userId'))
})
test('validate: create-identity rejects missing identityId', async (t) => {
const res = validateOp({ type: OP_CREATE_IDENTITY, method: 'ssh-key', timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('identityId'))
})
test('validate: create-identity rejects invalid method', async (t) => {
const res = validateOp({ type: OP_CREATE_IDENTITY, identityId: 'id1', method: 'github-oauth', timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('method'))
})
test('validate: create-identity rejects missing method', async (t) => {
const res = validateOp({ type: OP_CREATE_IDENTITY, identityId: 'id1', timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('method'))
})
test('validate: link-identity rejects missing identityId', async (t) => {
const res = validateOp({ type: OP_LINK_IDENTITY, userId: 'u1', timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('identityId'))
})
test('validate: link-identity rejects missing userId', async (t) => {
const res = validateOp({ type: OP_LINK_IDENTITY, identityId: 'id1', timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('userId'))
})
test('validate: unlink-identity rejects missing identityId', async (t) => {
const res = validateOp({ type: OP_UNLINK_IDENTITY, timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('identityId'))
})
test('validate: revoke-identity rejects missing identityId', async (t) => {
const res = validateOp({ type: OP_REVOKE_IDENTITY, timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('identityId'))
})
test('validate: restore-identity rejects missing identityId', async (t) => {
const res = validateOp({ type: OP_RESTORE_IDENTITY, timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('identityId'))
})
test('validate: upgrade-permissions rejects missing userId', async (t) => {
const res = validateOp({ type: OP_UPGRADE_PERMISSIONS, repoName: 'r', permissions: [], timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('userId'))
})
test('validate: upgrade-permissions rejects missing repoName', async (t) => {
const res = validateOp({ type: OP_UPGRADE_PERMISSIONS, userId: 'u1', permissions: [], timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('repoName'))
})
test('validate: upgrade-permissions rejects non-array permissions', async (t) => {
const res = validateOp({ type: OP_UPGRADE_PERMISSIONS, userId: 'u1', repoName: 'r', permissions: 'write', timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('permissions'))
})
test('validate: merge-accounts rejects missing primaryUserId', async (t) => {
const res = validateOp({ type: OP_MERGE_ACCOUNTS, secondaryUserId: 'u2', timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('primaryUserId'))
})
test('validate: merge-accounts rejects missing secondaryUserId', async (t) => {
const res = validateOp({ type: OP_MERGE_ACCOUNTS, primaryUserId: 'u1', timestamp: 1 })
t.is(res.valid, false)
t.ok(res.error.includes('secondaryUserId'))
})