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
3 changes: 2 additions & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"@colibri/core": "jsr:@colibri/core@^0.20.2",
"@moonlight/moonlight-sdk": "jsr:@moonlight/moonlight-sdk@^0.8.0",
"@opentelemetry/api": "npm:@opentelemetry/api@^1.9.0",
"stellar-sdk": "npm:@stellar/stellar-sdk@^15.0.1"
"stellar-sdk": "npm:@stellar/stellar-sdk@^15.0.1",
"postgres": "npm:postgres@3.4.7"
}
}
9 changes: 7 additions & 2 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion docker-compose.pos-instant.yml
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ services:
- |
cp /pos-src/*.ts /pos-src/deno.json . && cp /pos-src/deno.lock . 2>/dev/null || true
mkdir -p e2e && cp /e2e-src/*.ts /e2e-src/deno.json e2e/ 2>/dev/null || true
mkdir -p lib && cp /lib-src/*.ts lib/
mkdir -p lib && cp /lib-src/*.ts lib/ 2>/dev/null || true
cp -r /lib-src/client lib/
mkdir -p moonlight-pay-lib && cp /moonlight-pay-lib/*.ts moonlight-pay-lib/
deno install
echo "Waiting for provider, council, and pay-platform..."
Expand Down
37 changes: 18 additions & 19 deletions e2e/dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,12 @@ export async function dashboardE2E(
const token = await dashboardAuth(providerUrl, keypair);
console.log(" [dashboard] Authenticated");

// 2. Channels
const ppPath = `/providers/${encodeURIComponent(keypair.publicKey())}`;

// 2. Channels (per-PP)
await withE2ESpan("dashboard.channels", async () => {
console.log(" [dashboard] GET /dashboard/channels...");
const res = await fetchDashboard(providerUrl, token, "/dashboard/channels");
console.log(` [dashboard] GET ${ppPath}/channels...`);
const res = await fetchDashboard(providerUrl, token, `${ppPath}/channels`);
assertField(res, "data.channels");
assertField(res, "data.summary.total");
assertField(res, "data.summary.active");
Expand All @@ -123,10 +125,11 @@ export async function dashboardE2E(
console.log(" [dashboard] Channels OK");
});

// 3. Mempool
// 3. Mempool (per-PP — averages filtered to this PP; live stats still
// process-wide because the in-memory mempool is platform-shared.)
await withE2ESpan("dashboard.mempool", async () => {
console.log(" [dashboard] GET /dashboard/mempool...");
const res = await fetchDashboard(providerUrl, token, "/dashboard/mempool");
console.log(` [dashboard] GET ${ppPath}/mempool...`);
const res = await fetchDashboard(providerUrl, token, `${ppPath}/mempool`);
assertField(res, "data.platformVersion");
assertField(res, "data.live.totalSlots");
assertField(res, "data.live.totalBundles");
Expand All @@ -135,13 +138,13 @@ export async function dashboardE2E(
console.log(" [dashboard] Mempool OK");
});

// 4. Operations
// 4. Operations (per-PP bundle + tx counts)
await withE2ESpan("dashboard.operations", async () => {
console.log(" [dashboard] GET /dashboard/operations...");
console.log(` [dashboard] GET ${ppPath}/operations...`);
const res = await fetchDashboard(
providerUrl,
token,
"/dashboard/operations",
`${ppPath}/operations`,
);
assertField(res, "data.bundles.total");
assertField(res, "data.bundles.successRate");
Expand All @@ -151,24 +154,20 @@ export async function dashboardE2E(

// 5. Treasury
await withE2ESpan("dashboard.treasury", async () => {
console.log(" [dashboard] GET /dashboard/treasury...");
const res = await fetchDashboard(
providerUrl,
token,
`/dashboard/treasury?ppPublicKey=${keypair.publicKey()}`,
);
console.log(` [dashboard] GET ${ppPath}/treasury...`);
const res = await fetchDashboard(providerUrl, token, `${ppPath}/treasury`);
assertField(res, "data.address");
assertField(res, "data.balances");
console.log(" [dashboard] Treasury OK");
});

// 6. Audit export
// 6. Audit export (per-PP CSV)
await withE2ESpan("dashboard.audit_export", async () => {
console.log(" [dashboard] GET /dashboard/audit-export...");
console.log(` [dashboard] GET ${ppPath}/audit-export...`);
const csv = await fetchDashboard(
providerUrl,
token,
"/dashboard/audit-export?status=COMPLETED",
`${ppPath}/audit-export?status=COMPLETED`,
) as string;
if (typeof csv !== "string" || !csv.startsWith("id,")) {
throw new Error(
Expand All @@ -183,7 +182,7 @@ export async function dashboardE2E(
// 7. Verify unauthenticated access is blocked
await withE2ESpan("dashboard.auth_required", async () => {
console.log(" [dashboard] Verifying auth is required...");
const res = await fetch(`${providerUrl}/api/v1/dashboard/channels`);
const res = await fetch(`${providerUrl}/api/v1${ppPath}/channels`);
if (res.status !== 401 && res.status !== 403) {
throw new Error(
`Expected 401/403 for unauthenticated request, got ${res.status}`,
Expand Down
155 changes: 93 additions & 62 deletions e2e/governance/uc2-approve-reject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ async function waitForMembershipStatus(
while (Date.now() - start < timeoutMs) {
const m = await get(
PROVIDER_API,
`/dashboard/council/membership?ppPublicKey=${ppPublicKey}`,
`/providers/${ppPublicKey}/council/membership`,
token,
);
if (m.data.data?.status === expectedStatus) return true;
Expand Down Expand Up @@ -220,19 +220,23 @@ const j1Envelope = await signJoinPayload({
contactEmail: "a@t",
jurisdictions: null,
}, pp1);
const j1 = await post(PROVIDER_API, "/dashboard/council/join", {
councilUrl: COUNCIL_URL,
councilId: AUTH_ID,
councilName: "Test Council",
councilPublicKey: councilKp.publicKey(),
ppPublicKey: pp1.publicKey(),
signedEnvelope: j1Envelope,
}, dashToken);
const j1 = await post(
PROVIDER_API,
`/providers/${pp1.publicKey()}/council/join`,
{
councilUrl: COUNCIL_URL,
councilId: AUTH_ID,
councilName: "Test Council",
councilPublicKey: councilKp.publicKey(),
signedEnvelope: j1Envelope,
},
dashToken,
);
check("Join submitted", j1.data.data?.status, "PENDING");

const m1pre = await get(
PROVIDER_API,
`/dashboard/council/membership?ppPublicKey=${pp1.publicKey()}`,
`/providers/${pp1.publicKey()}/council/membership`,
dashToken,
);
check("Provider: PENDING before approve", m1pre.data.data?.status, "PENDING");
Expand Down Expand Up @@ -305,14 +309,18 @@ const j2Envelope = await signJoinPayload({
contactEmail: "r@t",
jurisdictions: null,
}, pp2);
const j2 = await post(PROVIDER_API, "/dashboard/council/join", {
councilUrl: COUNCIL_URL,
councilId: AUTH_ID,
councilName: "Test Council",
councilPublicKey: councilKp.publicKey(),
ppPublicKey: pp2.publicKey(),
signedEnvelope: j2Envelope,
}, dashToken);
const j2 = await post(
PROVIDER_API,
`/providers/${pp2.publicKey()}/council/join`,
{
councilUrl: COUNCIL_URL,
councilId: AUTH_ID,
councilName: "Test Council",
councilPublicKey: councilKp.publicKey(),
signedEnvelope: j2Envelope,
},
dashToken,
);
check("Join submitted", j2.data.data?.status, "PENDING");

const p2 = await get(
Expand Down Expand Up @@ -349,9 +357,12 @@ const ms2 = await fetch(
check("Membership-status: REJECTED (404)", ms2.status, 404);

// Sync rejection to provider-platform (simulates UI polling)
const sync2 = await post(PROVIDER_API, "/dashboard/council/membership", {
ppPublicKey: pp2.publicKey(),
}, dashToken);
const sync2 = await post(
PROVIDER_API,
`/providers/${pp2.publicKey()}/council/membership`,
{},
dashToken,
);
check("Provider synced to REJECTED", sync2.data.data?.status, "REJECTED");

// --- Test 3: PP with no council ---
Expand All @@ -364,7 +375,7 @@ await post(PROVIDER_API, "/dashboard/pp/register", {
}, dashToken);
const m3 = await get(
PROVIDER_API,
`/dashboard/council/membership?ppPublicKey=${pp3.publicKey()}`,
`/providers/${pp3.publicKey()}/council/membership`,
dashToken,
);
check("No membership", m3.data.data, null);
Expand Down Expand Up @@ -413,14 +424,18 @@ const j6aEnvelope = await signJoinPayload({
contactEmail: "dup@t",
jurisdictions: null,
}, pp6);
const j6a = await post(PROVIDER_API, "/dashboard/council/join", {
councilUrl: COUNCIL_URL,
councilId: AUTH_ID,
councilName: "Test Council",
councilPublicKey: councilKp.publicKey(),
ppPublicKey: pp6.publicKey(),
signedEnvelope: j6aEnvelope,
}, dashToken);
const j6a = await post(
PROVIDER_API,
`/providers/${pp6.publicKey()}/council/join`,
{
councilUrl: COUNCIL_URL,
councilId: AUTH_ID,
councilName: "Test Council",
councilPublicKey: councilKp.publicKey(),
signedEnvelope: j6aEnvelope,
},
dashToken,
);
check("First join succeeds", j6a.data.data?.status, "PENDING");

const j6bEnvelope = await signJoinPayload({
Expand All @@ -430,14 +445,18 @@ const j6bEnvelope = await signJoinPayload({
contactEmail: "dup@t",
jurisdictions: null,
}, pp6);
const j6b = await post(PROVIDER_API, "/dashboard/council/join", {
councilUrl: COUNCIL_URL,
councilId: AUTH_ID,
councilName: "Test Council",
councilPublicKey: councilKp.publicKey(),
ppPublicKey: pp6.publicKey(),
signedEnvelope: j6bEnvelope,
}, dashToken);
const j6b = await post(
PROVIDER_API,
`/providers/${pp6.publicKey()}/council/join`,
{
councilUrl: COUNCIL_URL,
councilId: AUTH_ID,
councilName: "Test Council",
councilPublicKey: councilKp.publicKey(),
signedEnvelope: j6bEnvelope,
},
dashToken,
);
check("Duplicate join returns 409", j6b.status, 409);

// --- Test 7: Membership-status edge cases ---
Expand Down Expand Up @@ -474,14 +493,18 @@ const j8aEnvelope = await signJoinPayload({
contactEmail: null,
jurisdictions: null,
}, pp8a);
const j8a = await post(PROVIDER_API, "/dashboard/council/join", {
councilUrl: COUNCIL_URL,
councilId: AUTH_ID,
councilName: "Test Council",
councilPublicKey: councilKp.publicKey(),
ppPublicKey: pp8a.publicKey(),
signedEnvelope: j8aEnvelope,
}, dashToken);
const j8a = await post(
PROVIDER_API,
`/providers/${pp8a.publicKey()}/council/join`,
{
councilUrl: COUNCIL_URL,
councilId: AUTH_ID,
councilName: "Test Council",
councilPublicKey: councilKp.publicKey(),
signedEnvelope: j8aEnvelope,
},
dashToken,
);
check("PP-Multi-A join", j8a.data.data?.status, "PENDING");

const j8bEnvelope = await signJoinPayload({
Expand All @@ -491,14 +514,18 @@ const j8bEnvelope = await signJoinPayload({
contactEmail: null,
jurisdictions: null,
}, pp8b);
const j8b = await post(PROVIDER_API, "/dashboard/council/join", {
councilUrl: COUNCIL_URL,
councilId: AUTH_ID,
councilName: "Test Council",
councilPublicKey: councilKp.publicKey(),
ppPublicKey: pp8b.publicKey(),
signedEnvelope: j8bEnvelope,
}, dashToken);
const j8b = await post(
PROVIDER_API,
`/providers/${pp8b.publicKey()}/council/join`,
{
councilUrl: COUNCIL_URL,
councilId: AUTH_ID,
councilName: "Test Council",
councilPublicKey: councilKp.publicKey(),
signedEnvelope: j8bEnvelope,
},
dashToken,
);
check("PP-Multi-B join", j8b.data.data?.status, "PENDING");

// Approve both
Expand Down Expand Up @@ -575,14 +602,18 @@ const j9Envelope = await signJoinPayload({
contactEmail: null,
jurisdictions: null,
}, pp9);
const j9 = await post(PROVIDER_API, "/dashboard/council/join", {
councilUrl: COUNCIL_URL,
councilId: council2Id,
councilName: "Council 2",
councilPublicKey: councilKp.publicKey(),
ppPublicKey: pp9.publicKey(),
signedEnvelope: j9Envelope,
}, dashToken);
const j9 = await post(
PROVIDER_API,
`/providers/${pp9.publicKey()}/council/join`,
{
councilUrl: COUNCIL_URL,
councilId: council2Id,
councilName: "Council 2",
councilPublicKey: councilKp.publicKey(),
signedEnvelope: j9Envelope,
},
dashToken,
);
check("Join council 2", j9.data.data?.status, "PENDING");

// Council 2 requests should only show PP-Council2
Expand Down
Loading
Loading