Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions api/admin/claim.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,35 @@ module.exports = async function handler(req, res) {
}

try {
const claimResult = await db.query('select * from claim_requests where claim_id = $1 limit 1', [claimId]);
if (!claimResult.rows.length) {
const claimRows = db.normalizeRows(
await db.query('select * from claim_requests where claim_id = $1 limit 1', [claimId])
);
if (!claimRows.length) {
return res.status(404).json({ ok: false, status: 'CLAIM_NOT_FOUND' });
}

const agentsResult = await db.query('select * from claim_agents where claim_id = $1 order by capability asc', [claimId]);
const eventsResult = await db.query('select * from claim_events where claim_id = $1 order by created_at asc', [claimId]);
const agentRows = db.normalizeRows(
await db.query('select * from claim_agents where claim_id = $1 order by capability asc', [claimId])
);
const eventRows = db.normalizeRows(
await db.query('select * from claim_events where claim_id = $1 order by created_at asc', [claimId])
);

return res.status(200).json({
ok: true,
claim: claimResult.rows[0],
agents: agentsResult.rows,
events: eventsResult.rows
claim: claimRows[0],
agents: agentRows,
events: eventRows
});
} catch (error) {
console.error('ADMIN_CLAIM_QUERY_FAILED', { message: error.message, code: error.code });
return res.status(500).json({ ok: false, status: 'ADMIN_CLAIM_QUERY_FAILED', error: 'Failed to load claim.' });
const payload = { ok: false, status: 'ADMIN_CLAIM_QUERY_FAILED', error: 'Failed to load claim.' };
if (process.env.NODE_ENV !== 'production') {
payload.debug = {
message: error.message,
code: error.code
};
}
return res.status(500).json(payload);
}
};
12 changes: 10 additions & 2 deletions api/admin/claims.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ module.exports = async function handler(req, res) {
[limit]
);

const claims = result.rows.map((row) => ({
const rows = db.normalizeRows(result);
const claims = rows.map((row) => ({
claimId: row.claim_id,
tenant: row.tenant,
authenticatedAddress: row.authenticated_address,
Expand All @@ -61,6 +62,13 @@ module.exports = async function handler(req, res) {
return res.status(200).json({ ok: true, claims });
} catch (error) {
console.error('ADMIN_CLAIMS_QUERY_FAILED', { message: error.message, code: error.code });
return res.status(500).json({ ok: false, status: 'ADMIN_CLAIMS_QUERY_FAILED', error: 'Failed to load claims.' });
const payload = { ok: false, status: 'ADMIN_CLAIMS_QUERY_FAILED', error: 'Failed to load claims.' };
if (process.env.NODE_ENV !== 'production') {
payload.debug = {
message: error.message,
code: error.code
};
}
return res.status(500).json(payload);
}
};
27 changes: 25 additions & 2 deletions lib/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,37 @@ function getDatabaseUrl() {
return databaseUrl;
}

function normalizeRows(result) {
if (Array.isArray(result)) {
return result;
}
if (result && Array.isArray(result.rows)) {
return result.rows;
}
return [];
}

function normalizeQueryResult(result) {
if (Array.isArray(result)) {
return { rows: result };
}
if (result && Array.isArray(result.rows)) {
return result;
}
return { rows: [] };
}

async function query(text, params = []) {
// Lazy-load Neon so tests can mock this module without requiring installed drivers.
const { neon } = require('@neondatabase/serverless');
const sql = neon(getDatabaseUrl());
return sql.query(text, params);
const result = await sql.query(text, params);
return normalizeQueryResult(result);
}

module.exports = {
query,
getDatabaseUrl
getDatabaseUrl,
normalizeRows,
normalizeQueryResult
};
63 changes: 60 additions & 3 deletions tests/api-admin-claims.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,24 @@ function makeRes() {
};
}

function normalizeRows(result) {
if (Array.isArray(result)) return result;
if (result && Array.isArray(result.rows)) return result.rows;
return [];
}

function load(modulePath, mockQuery) {
const handlerPath = require.resolve(modulePath);
const dbPath = require.resolve('../lib/db');
delete require.cache[handlerPath];
delete require.cache[dbPath];
require.cache[dbPath] = { exports: { query: mockQuery, getDatabaseUrl: () => process.env.DATABASE_URL } };
require.cache[dbPath] = {
exports: {
query: mockQuery,
normalizeRows,
getDatabaseUrl: () => process.env.DATABASE_URL
}
};
return require(modulePath);
}

Expand All @@ -41,7 +53,7 @@ test('admin claims returns UNAUTHORIZED when auth missing', async () => {
assert.equal(res.body.status, 'UNAUTHORIZED');
});

test('admin claims returns list when authorized', async () => {
test('admin claims returns list when authorized (pg style result)', async () => {
process.env.ADMIN_API_KEY = 'secret';
const handler = load('../api/admin/claims', async () => ({
rows: [{
Expand Down Expand Up @@ -71,7 +83,35 @@ test('admin claims returns list when authorized', async () => {
});
});

test('admin claim detail returns agents and events when authorized', async () => {
test('admin claims returns list when authorized (neon direct-array result)', async () => {
process.env.ADMIN_API_KEY = 'secret';
const handler = load('../api/admin/claims', async () => ([{
claim_id: 'clm_test',
tenant: 'tenant',
authenticated_address: '0xabc',
activation_mode: 'cl',
pack_id: 'trust',
status: 'created',
created_at: '2026-05-23T00:00:00Z',
agent_count: 10
}]));
const res = makeRes();
await handler({ method: 'GET', headers: { authorization: 'Bearer secret' }, query: {} }, res);
assert.equal(res.statusCode, 200);
assert.equal(res.body.ok, true);
assert.deepEqual(res.body.claims[0], {
claimId: 'clm_test',
tenant: 'tenant',
authenticatedAddress: '0xabc',
activationMode: 'cl',
packId: 'trust',
status: 'created',
agentCount: 10,
createdAt: '2026-05-23T00:00:00Z'
});
});

test('admin claim detail returns agents and events when authorized (pg style result)', async () => {
process.env.ADMIN_API_KEY = 'secret';
const handler = load('../api/admin/claim', async (text) => {
const q = String(text);
Expand All @@ -86,3 +126,20 @@ test('admin claim detail returns agents and events when authorized', async () =>
assert.equal(Array.isArray(res.body.agents), true);
assert.equal(Array.isArray(res.body.events), true);
});

test('admin claim detail returns agents and events when authorized (neon direct-array result)', async () => {
process.env.ADMIN_API_KEY = 'secret';
const handler = load('../api/admin/claim', async (text) => {
const q = String(text);
if (q.includes('from claim_requests')) return [{ claim_id: 'clm_1', tenant: 'commandlayer', request_json: {} }];
if (q.includes('from claim_agents')) return [{ ens: 'x.signagent.eth' }];
return [{ event_type: 'claim.created' }];
});
const res = makeRes();
await handler({ method: 'GET', headers: { authorization: 'Bearer secret' }, query: { claimId: 'clm_1' } }, res);
assert.equal(res.statusCode, 200);
assert.equal(res.body.ok, true);
assert.deepEqual(res.body.claim, { claim_id: 'clm_1', tenant: 'commandlayer', request_json: {} });
assert.deepEqual(res.body.agents, [{ ens: 'x.signagent.eth' }]);
assert.deepEqual(res.body.events, [{ event_type: 'claim.created' }]);
});
Loading