feat: add unified resale dashboard
This commit is contained in:
@@ -201,6 +201,9 @@ describe("SurplusClient", () => {
|
||||
healthy: true,
|
||||
trusted: true,
|
||||
trades_24h: 7,
|
||||
rank: 1,
|
||||
cap_remaining: null,
|
||||
volume_24h: 0,
|
||||
}],
|
||||
});
|
||||
});
|
||||
@@ -222,6 +225,79 @@ describe("SurplusClient", () => {
|
||||
expect(String(fetchFn.mock.calls[1][0])).toContain("alpha%3Amodel");
|
||||
});
|
||||
|
||||
it("normalizes live earnings, payout hold, and settlement records", async () => {
|
||||
const client = new SurplusClient({
|
||||
apiBaseUrl: "https://api.surplusintelligence.ai",
|
||||
sellerApiKey: SELLER_KEY,
|
||||
timeoutMs: 1_000,
|
||||
fetchFn: async () => Response.json({
|
||||
total_earned_usdc: "186810",
|
||||
pending_usdc: "176817",
|
||||
paid_usdc: "9993",
|
||||
daily: [{
|
||||
day: "2026-08-27",
|
||||
earned_usd: 0.18681,
|
||||
input_tokens: 100,
|
||||
output_tokens: 20,
|
||||
requests: 3,
|
||||
total_tokens: 120,
|
||||
}],
|
||||
by_model: [{
|
||||
model: "alpha",
|
||||
earned_usd: 0.18681,
|
||||
requests: 3,
|
||||
total_tokens: 120,
|
||||
last_sale_at: 1787831146968,
|
||||
}],
|
||||
share: {
|
||||
requests: 3,
|
||||
tokens: 120,
|
||||
top_model: "alpha",
|
||||
},
|
||||
recent_sales: [{
|
||||
id: "sale-1",
|
||||
model: "alpha",
|
||||
offer_id: "offer-1",
|
||||
settlement_status: "confirmed",
|
||||
created_at: 1787831146968,
|
||||
seller_cost_usdc: "83",
|
||||
input_tokens: 10,
|
||||
output_tokens: 2,
|
||||
cache_read_tokens: 4,
|
||||
effective_input_per_1m: 0.004,
|
||||
effective_output_per_1m: 0.008,
|
||||
tx_hash: "0x" + "a".repeat(64),
|
||||
}],
|
||||
payout_hold: {
|
||||
reason: "new_seller",
|
||||
releases_at: "2026-08-29T11:24:42.552Z",
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const earnings = await client.getEarnings();
|
||||
|
||||
expect(earnings).toMatchObject({
|
||||
totalEarnedMicroUsd: 186_810,
|
||||
pendingMicroUsd: 176_817,
|
||||
paidMicroUsd: 9_993,
|
||||
requestCount: 3,
|
||||
tokenCount: 120,
|
||||
topModel: "alpha",
|
||||
payoutHoldReason: "new_seller",
|
||||
});
|
||||
expect(earnings.daily[0]).toMatchObject({ requests: 3, totalTokens: 120 });
|
||||
expect(earnings.recentSales[0]).toMatchObject({
|
||||
id: "sale-1",
|
||||
settlementStatus: "confirmed",
|
||||
cacheReadTokens: 4,
|
||||
sellerCostMicroUsd: 83,
|
||||
});
|
||||
expect(earnings.recentSales[0].createdAt).toBe(
|
||||
new Date(1787831146968).toISOString(),
|
||||
);
|
||||
});
|
||||
|
||||
it("sends one absolute cost-multiplier PATCH with the cap", async () => {
|
||||
let request: RequestInit | undefined;
|
||||
const client = new SurplusClient({
|
||||
|
||||
@@ -114,6 +114,9 @@ class FakeSurplusClient implements SurplusControllerClient {
|
||||
healthy: true,
|
||||
trusted: true,
|
||||
trades24h: 0,
|
||||
rank: 1,
|
||||
capRemainingMicroUsd: null,
|
||||
volume24hMicroUsd: 0,
|
||||
}];
|
||||
if (this.offer) {
|
||||
offers.push({
|
||||
@@ -126,6 +129,9 @@ class FakeSurplusClient implements SurplusControllerClient {
|
||||
healthy: true,
|
||||
trusted: true,
|
||||
trades24h: this.trades24h,
|
||||
rank: 2,
|
||||
capRemainingMicroUsd: 50_000,
|
||||
volume24hMicroUsd: 0,
|
||||
});
|
||||
}
|
||||
return { model, offers };
|
||||
@@ -187,12 +193,26 @@ class FakeSurplusClient implements SurplusControllerClient {
|
||||
totalEarnedMicroUsd: this.settled ? 100 : 0,
|
||||
pendingMicroUsd: 0,
|
||||
paidMicroUsd: this.settled ? 100 : 0,
|
||||
daily: [],
|
||||
byModel: [],
|
||||
requestCount: this.settled ? 1 : 0,
|
||||
tokenCount: this.settled ? 2 : 0,
|
||||
topModel: this.settled ? "beta-model" : null,
|
||||
payoutHoldReason: null,
|
||||
payoutHoldReleasesAt: null,
|
||||
recentSales: this.settled ? [{
|
||||
id: "sale-1",
|
||||
model: "beta-model",
|
||||
offerId: "managed-offer",
|
||||
settlementStatus: "confirmed",
|
||||
createdAt: "2026-08-27T10:00:01.000Z",
|
||||
sellerCostMicroUsd: 100,
|
||||
inputTokens: 1,
|
||||
outputTokens: 1,
|
||||
cacheReadTokens: 0,
|
||||
effectiveInputUsdPer1m: 0.003972,
|
||||
effectiveOutputUsdPer1m: 0.007944,
|
||||
transactionHash: "0x" + "a".repeat(64),
|
||||
}] : [],
|
||||
};
|
||||
}
|
||||
|
||||
259
tests/unit/surplus-dashboard.test.ts
Normal file
259
tests/unit/surplus-dashboard.test.ts
Normal file
@@ -0,0 +1,259 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import request from "supertest";
|
||||
import type { SurplusManagerConfig } from "../../supplier/src/surplus/config.js";
|
||||
import {
|
||||
SurplusDashboardService,
|
||||
type SurplusDashboardSnapshot,
|
||||
} from "../../supplier/src/surplus/dashboard.js";
|
||||
import { renderSurplusDashboardPage } from "../../supplier/src/surplus/dashboardPage.js";
|
||||
import { createSurplusServer } from "../../supplier/src/cli/surplus-seller.js";
|
||||
import type { SurplusControllerStatus } from "../../supplier/src/surplus/controller.js";
|
||||
|
||||
const STATUS: SurplusControllerStatus = {
|
||||
ok: true,
|
||||
phase: "active",
|
||||
live: true,
|
||||
model: "deepseek-v4-flash",
|
||||
providerModelId: "deepseek/deepseek-v4-flash",
|
||||
offerId: "offer-1",
|
||||
inputUsdPer1m: "0.003972",
|
||||
outputUsdPer1m: "0.007944",
|
||||
competitorId: "external",
|
||||
remainingAllowanceUsd: "16.313443",
|
||||
reserveThresholdUsd: "1.00",
|
||||
totalEarnedUsd: "0.186810",
|
||||
lastCycleAt: "2026-08-28T12:00:00.000Z",
|
||||
lastSuccessAt: "2026-08-28T12:00:00.000Z",
|
||||
lastError: null,
|
||||
};
|
||||
|
||||
function config(): SurplusManagerConfig {
|
||||
return {
|
||||
live: true,
|
||||
apiBaseUrl: "https://api.surplusintelligence.ai",
|
||||
sellerApiKey: "si_seller_test_secret",
|
||||
providerApiKey: "sk-or-v1-test-secret",
|
||||
providerBaseUrl: "https://openrouter.ai/api/v1",
|
||||
capacityBaseUrl: "https://openrouter.ai/api",
|
||||
perOfferCapUsd: "0.05",
|
||||
aggregateCapUsd: "0.05",
|
||||
sellerWallet: "0x1111111111111111111111111111111111111111",
|
||||
payoutAddress: "0x1111111111111111111111111111111111111111",
|
||||
maxCandidateOrderBooks: 10,
|
||||
reserveUsd: "1.00",
|
||||
maxProviderLimitUsd: "20.00",
|
||||
recoveryBps: 490,
|
||||
undercutBps: 10,
|
||||
pollIntervalMs: 300_000,
|
||||
requestTimeoutMs: 120_000,
|
||||
stopAfterTrades: 0,
|
||||
settledStatuses: ["confirmed"],
|
||||
statePath: "/tmp/surplus-dashboard-test.json",
|
||||
port: 8080,
|
||||
};
|
||||
}
|
||||
|
||||
function snapshot(): SurplusDashboardSnapshot {
|
||||
return {
|
||||
generatedAt: "2026-08-28T12:00:00.000Z",
|
||||
controller: STATUS,
|
||||
identity: {
|
||||
sellerWallet: "0x1111111111111111111111111111111111111111",
|
||||
payoutAddress: "0x1111111111111111111111111111111111111111",
|
||||
},
|
||||
provider: {
|
||||
name: "OpenRouter",
|
||||
hardLimitUsd: "20.000000",
|
||||
remainingUsd: "16.313443",
|
||||
usedUsd: "3.686557",
|
||||
reserveUsd: "1.00",
|
||||
estimatedDailyExposureUsd: "1.020408",
|
||||
},
|
||||
surplus: {
|
||||
offer: {
|
||||
id: "offer-1",
|
||||
model: "deepseek-v4-flash",
|
||||
providerModel: "deepseek/deepseek-v4-flash",
|
||||
status: "active",
|
||||
dailyCapUsd: "0.050000",
|
||||
costMultiplier: 0.04995,
|
||||
inputUsdPer1m: "0.003972",
|
||||
outputUsdPer1m: "0.007944",
|
||||
rank: 10,
|
||||
available: true,
|
||||
healthy: true,
|
||||
trusted: true,
|
||||
trades24h: 429,
|
||||
volume24hUsd: "0.064030",
|
||||
capRemainingUsd: "0.050000",
|
||||
},
|
||||
earnings: {
|
||||
totalUsd: "0.186810",
|
||||
pendingUsd: "0.176817",
|
||||
paidUsd: "0.009993",
|
||||
requests: 1_093,
|
||||
tokens: 104_698_905,
|
||||
today: null,
|
||||
topModel: "deepseek-v4-flash",
|
||||
payoutHoldReason: "new_seller",
|
||||
payoutHoldReleasesAt: "2026-08-29T11:24:42.552Z",
|
||||
},
|
||||
recentSales: [],
|
||||
},
|
||||
vector: {
|
||||
status: "retired",
|
||||
model: "deepseek/deepseek-v4-flash",
|
||||
supplierWallet: "addr1test",
|
||||
advertRef: `${"a".repeat(64)}#0`,
|
||||
retirementTransaction: "b".repeat(64),
|
||||
retiredOn: "2026-08-27",
|
||||
historicalAp3xEarned: "1.600000",
|
||||
historicalSettledJobs: 8,
|
||||
historicalUpstreamSpendUsd: "0.000093",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("SurplusDashboardService", () => {
|
||||
it("combines safe live metrics with the Vector migration record", async () => {
|
||||
const listAllOffers = vi.fn(async () => [{
|
||||
id: "offer-1",
|
||||
model: "deepseek-v4-flash",
|
||||
sellerBaseUrl: "https://openrouter.ai/api/v1",
|
||||
status: "active" as const,
|
||||
capDailyUsd: 0.05,
|
||||
costMultiplierPpm: 49_950,
|
||||
inputMicroUsdPer1m: 3_972,
|
||||
outputMicroUsdPer1m: 7_944,
|
||||
}]);
|
||||
const getEarnings = vi.fn(async () => ({
|
||||
totalEarnedMicroUsd: 186_810,
|
||||
pendingMicroUsd: 176_817,
|
||||
paidMicroUsd: 9_993,
|
||||
daily: [{
|
||||
day: "2026-08-28",
|
||||
earnedUsd: 0.01,
|
||||
inputTokens: 10,
|
||||
outputTokens: 2,
|
||||
requests: 1,
|
||||
totalTokens: 12,
|
||||
}],
|
||||
byModel: [],
|
||||
requestCount: 1_093,
|
||||
tokenCount: 104_698_905,
|
||||
topModel: "deepseek-v4-flash",
|
||||
payoutHoldReason: "new_seller",
|
||||
payoutHoldReleasesAt: "2026-08-29T11:24:42.552Z",
|
||||
recentSales: [{
|
||||
id: "sale-1",
|
||||
model: "deepseek-v4-flash",
|
||||
offerId: "offer-1",
|
||||
settlementStatus: "confirmed",
|
||||
createdAt: "2026-08-28T11:00:00.000Z",
|
||||
sellerCostMicroUsd: 83,
|
||||
inputTokens: 10,
|
||||
outputTokens: 2,
|
||||
cacheReadTokens: 3,
|
||||
effectiveInputUsdPer1m: 0.003972,
|
||||
effectiveOutputUsdPer1m: 0.007944,
|
||||
transactionHash: "0x" + "c".repeat(64),
|
||||
}],
|
||||
}));
|
||||
const getOrderBook = vi.fn(async () => ({
|
||||
model: "deepseek-v4-flash",
|
||||
offers: [{
|
||||
id: "offer-1",
|
||||
seller: "0x1111111111111111111111111111111111111111",
|
||||
sellerBaseUrl: "https://openrouter.ai/api/v1",
|
||||
inputMicroUsdPer1m: 3_972,
|
||||
outputMicroUsdPer1m: 7_944,
|
||||
available: true,
|
||||
healthy: true,
|
||||
trusted: true,
|
||||
trades24h: 429,
|
||||
rank: 10,
|
||||
capRemainingMicroUsd: 50_000,
|
||||
volume24hMicroUsd: 64_030,
|
||||
}],
|
||||
}));
|
||||
const service = new SurplusDashboardService({
|
||||
controller: { snapshot: () => STATUS },
|
||||
client: { listAllOffers, getEarnings, getOrderBook },
|
||||
allowance: {
|
||||
readAllowance: async () => ({
|
||||
limitUsdNanos: 20_000_000_000n,
|
||||
remainingUsdNanos: 16_313_443_131n,
|
||||
reset: null,
|
||||
checkedAtMs: Date.parse("2026-08-28T12:00:00.000Z"),
|
||||
}),
|
||||
},
|
||||
config: config(),
|
||||
now: () => Date.parse("2026-08-28T12:00:00.000Z"),
|
||||
});
|
||||
|
||||
const first = await service.getSnapshot();
|
||||
const second = await service.getSnapshot();
|
||||
|
||||
expect(first.surplus.offer).toMatchObject({
|
||||
id: "offer-1",
|
||||
rank: 10,
|
||||
available: true,
|
||||
dailyCapUsd: "0.050000",
|
||||
});
|
||||
expect(first.surplus.earnings).toMatchObject({
|
||||
totalUsd: "0.186810",
|
||||
requests: 1_093,
|
||||
topModel: "deepseek-v4-flash",
|
||||
});
|
||||
expect(first.provider).toMatchObject({
|
||||
usedUsd: "3.686556",
|
||||
estimatedDailyExposureUsd: "1.020408",
|
||||
});
|
||||
expect(first.vector).toMatchObject({ status: "retired", historicalSettledJobs: 8 });
|
||||
expect(first.surplus.recentSales[0]).toMatchObject({
|
||||
settlementStatus: "confirmed",
|
||||
revenueUsd: "0.000083",
|
||||
});
|
||||
expect(JSON.stringify(first)).not.toContain("si_seller_test_secret");
|
||||
expect(JSON.stringify(first)).not.toContain("sk-or-v1-test-secret");
|
||||
expect(second).toBe(first);
|
||||
expect(listAllOffers).toHaveBeenCalledTimes(1);
|
||||
expect(getEarnings).toHaveBeenCalledTimes(1);
|
||||
expect(getOrderBook).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("unified dashboard HTTP surface", () => {
|
||||
it("serves the dashboard, cached API data, and security headers", async () => {
|
||||
const data = snapshot();
|
||||
const server = createSurplusServer(
|
||||
{ snapshot: () => STATUS, healthy: () => true },
|
||||
{ getSnapshot: async () => data },
|
||||
);
|
||||
|
||||
const page = await request(server).get("/reseller");
|
||||
expect(page.status).toBe(200);
|
||||
expect(page.text).toContain("One capacity pool.");
|
||||
expect(page.text).toContain("Vector marketplace");
|
||||
expect(page.headers["content-security-policy"]).toContain("frame-ancestors 'none'");
|
||||
|
||||
const api = await request(server).get("/dashboard/api");
|
||||
expect(api.status).toBe(200);
|
||||
expect(api.body).toEqual(data);
|
||||
expect(JSON.stringify(api.body)).not.toContain("test-secret");
|
||||
|
||||
const root = await request(server).get("/");
|
||||
expect(root.status).toBe(302);
|
||||
expect(root.headers.location).toBe("/reseller");
|
||||
server.close();
|
||||
});
|
||||
|
||||
it("renders static HTML without embedding credentials", () => {
|
||||
const html = renderSurplusDashboardPage();
|
||||
expect(html).toContain("/dashboard/api");
|
||||
expect(html).toContain("Surplus Intelligence");
|
||||
expect(html).toContain("Vector marketplace");
|
||||
expect(html).not.toContain("si_seller_");
|
||||
expect(html).not.toContain("sk-or-v1-");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user