diff --git a/e2e/governance/uc2-approve-reject.ts b/e2e/governance/uc2-approve-reject.ts index 49a3e06..d190785 100644 --- a/e2e/governance/uc2-approve-reject.ts +++ b/e2e/governance/uc2-approve-reject.ts @@ -377,9 +377,12 @@ check("3 PPs", list.data.data?.length, 3); const la = list.data.data?.find((p: any) => p.label === "PP-Approve"); const lr = list.data.data?.find((p: any) => p.label === "PP-Reject"); const ln = list.data.data?.find((p: any) => p.label === "PP-None"); -check("PP-Approve: ACTIVE", la?.councilMembership?.status, "ACTIVE"); -check("PP-Reject: REJECTED", lr?.councilMembership?.status, "REJECTED"); -check("PP-None: no council", ln?.councilMembership, null); +// provider-platform PR #103 renamed councilMembership (singular) → +// councilMemberships (plural array). A PP can be a member of multiple +// councils; this test creates exactly one membership per PP. +check("PP-Approve: ACTIVE", la?.councilMemberships?.[0]?.status, "ACTIVE"); +check("PP-Reject: REJECTED", lr?.councilMemberships?.[0]?.status, "REJECTED"); +check("PP-None: no council", ln?.councilMemberships?.length, 0); // --- Test 5: Council shows both --- console.log("\n\x1b[34m=== Test 5: Council requests list ===\x1b[0m"); diff --git a/e2e/pos-instant/main.ts b/e2e/pos-instant/main.ts index 5c3525d..baf7354 100644 --- a/e2e/pos-instant/main.ts +++ b/e2e/pos-instant/main.ts @@ -82,6 +82,28 @@ await Promise.all([ ]); console.log(` Funded (${elapsed()})`); +// [1b] Register pay-service as an APPROVED entity on provider-platform. +// Pay-platform submits bundles to provider-platform under its PAY_SERVICE_SK +// identity; provider-platform now gates bundle admission on the submitter +// being an APPROVED entity, so this registration is required. +console.log("\n[1b/5] Registering pay-service as APPROVED entity..."); +const entityRes = await fetch(`${PROVIDER_URL}/api/v1/entities`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + pubkey: keys.payService.publicKey(), + name: "Pay Service", + jurisdictions: [], + }), +}); +// 409 = already APPROVED; treat as success for idempotency. +if (!entityRes.ok && entityRes.status !== 409) { + throw new Error( + `Entity registration failed: ${entityRes.status} ${await entityRes.text()}`, + ); +} +console.log(` Pay-service approved (${elapsed()})`); + // [2] Create merchant on pay-platform + store UTXOs console.log("\n[2/5] Creating merchant account + UTXOs..."); const merchantJwt = await getPayJwt( diff --git a/lifecycle/ci-test.ts b/lifecycle/ci-test.ts index 8e5adfd..9c027a5 100644 --- a/lifecycle/ci-test.ts +++ b/lifecycle/ci-test.ts @@ -60,6 +60,25 @@ function loadEnvFile(path: string): Record { return env; } +async function registerEntity( + providerUrl: string, + pubkey: string, + name: string, +): Promise { + const res = await fetch(`${providerUrl}/api/v1/entities`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ pubkey, name, jurisdictions: [] }), + }); + // 409 = already APPROVED; treat as success for idempotency. + if (!res.ok && res.status !== 409) { + throw new Error( + `Entity registration failed for ${pubkey}: ${res.status} ${await res + .text()}`, + ); + } +} + async function fundAccount(publicKey: string): Promise { const res = await fetch(`${FRIENDBOT_URL}?addr=${publicKey}`); if (!res.ok) { @@ -198,6 +217,9 @@ async function main() { horizonUrl, friendbotUrl: FRIENDBOT_URL, providerUrl: PROVIDER_URL, + // Bundles are URL-scoped to /providers/:ppPublicKey/bundles. Lifecycle + // CI runs against the single seeded PP, so ppPublicKey == ppOperator. + ppPublicKey: ppOperator.publicKey(), channelContractId: channelContractId as ContractId, channelAuthId: channelAuthId as ContractId, channelAssetContractId: assetContractId as ContractId, @@ -418,11 +440,15 @@ async function main() { const aliceJwt = await authenticate(alice, e2eConfig); console.log(" Alice authenticated"); + await registerEntity(PROVIDER_URL, alice.publicKey(), "Alice"); + console.log(" Alice approved as entity"); await deposit(alice.secret(), DEPOSIT_AMOUNT, aliceJwt, e2eConfig); console.log(` Deposit ${DEPOSIT_AMOUNT} XLM complete`); const bobJwt = await authenticate(bob, e2eConfig); console.log(" Bob authenticated"); + await registerEntity(PROVIDER_URL, bob.publicKey(), "Bob"); + console.log(" Bob approved as entity"); const receiverOps = await prepareReceive( bob.secret(), SEND_AMOUNT, diff --git a/test/setup-e2e.ts b/test/setup-e2e.ts index 7d6cfd3..85be850 100644 --- a/test/setup-e2e.ts +++ b/test/setup-e2e.ts @@ -244,6 +244,9 @@ E2E_CHANNEL_ASSET_CONTRACT_ID=${assetContractId} # Provider keypair (registered on-chain by setup) E2E_PROVIDER_PK=${provider.publicKey()} E2E_PROVIDER_SK=${provider.secret()} +# Bundles are URL-scoped to /providers/:ppPublicKey/bundles; in the single-PP +# e2e harness the PP key == the provider key. +E2E_PP_PUBLIC_KEY=${provider.publicKey()} # Admin/Council keypair (for governance tests) E2E_ADMIN_SK=${admin.secret()}