diff options
Diffstat (limited to 'packages/taler-wallet-cli/src/integrationtests')
| -rw-r--r-- | packages/taler-wallet-cli/src/integrationtests/test-merchant-spec-public-orders.ts | 393 | 
1 files changed, 337 insertions, 56 deletions
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-spec-public-orders.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-spec-public-orders.ts index 523f760dd..ab7bf1acd 100644 --- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-spec-public-orders.ts +++ b/packages/taler-wallet-cli/src/integrationtests/test-merchant-spec-public-orders.ts @@ -28,7 +28,14 @@ import {    NodeHttpLib,    WalletApiOperation,  } from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState, MerchantPrivateApi, WalletCli } from "./harness"; +import { +  BankService, +  ExchangeService, +  GlobalTestState, +  MerchantPrivateApi, +  MerchantService, +  WalletCli, +} from "./harness";  import {    createSimpleTestkudosEnvironment,    withdrawViaBank, @@ -36,65 +43,277 @@ import {  const httpLib = new NodeHttpLib(); -/** - * Checks for the /orders/{id} endpoint of the merchant. - * - * The tests here should exercise all code paths in the executable - * specification of the endpoint. - */ -export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) { -  const { -    wallet, -    bank, -    exchange, -    merchant, -  } = await createSimpleTestkudosEnvironment(t); -  const wallet2 = new WalletCli(t); - -  // Withdraw digital cash into the wallet. +interface Context { +  merchant: MerchantService; +  merchantBaseUrl: string; +  bank: BankService; +  exchange: ExchangeService; +} +async function testWithClaimToken( +  t: GlobalTestState, +  c: Context, +): Promise<void> { +  const wallet = new WalletCli(t, "withclaimtoken"); +  const { bank, exchange } = c; +  const { merchant, merchantBaseUrl } = c;    await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); -  await withdrawViaBank(t, { -    wallet: wallet2, -    bank, -    exchange, -    amount: "TESTKUDOS:20", +  const sessionId = "mysession"; +  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { +    order: { +      summary: "Buy me!", +      amount: "TESTKUDOS:5", +      fulfillment_url: "https://example.com/article42", +      public_reorder_url: "https://example.com/article42-share", +    },    }); -  // Base URL for the default instance. -  const merchantBaseUrl = merchant.makeInstanceBaseUrl(); + +  const claimToken = orderResp.token; +  const orderId = orderResp.order_id; +  t.assertTrue(!!claimToken); +  let talerPayUri: string;    { -    const httpResp = await httpLib.get(new URL("config", merchantBaseUrl).href); +    const httpResp = await httpLib.get( +      new URL(`orders/${orderId}`, merchantBaseUrl).href, +    );      const r = await httpResp.json(); +    t.assertDeepEqual(httpResp.status, 202);      console.log(r); -    t.assertDeepEqual(r.currency, "TESTKUDOS");    }    { -    const httpResp = await httpLib.get( -      new URL("orders/foo", merchantBaseUrl).href, -    ); +    const url = new URL(`orders/${orderId}`, merchantBaseUrl); +    url.searchParams.set("token", claimToken); +    const httpResp = await httpLib.get(url.href);      const r = await httpResp.json(); +    t.assertDeepEqual(httpResp.status, 402);      console.log(r); -    t.assertDeepEqual(httpResp.status, 404); -    // FIXME: also check Taler error code +    talerPayUri = r.taler_pay_uri; +    t.assertTrue(!!talerPayUri);    }    { -    const httpResp = await httpLib.get( -      new URL("orders/foo", merchantBaseUrl).href, -      { -        headers: { -          Accept: "text/html", -        }, +    const url = new URL(`orders/${orderId}`, merchantBaseUrl); +    url.searchParams.set("token", claimToken); +    const httpResp = await httpLib.get(url.href, { +      headers: { +        Accept: "text/html",        }, -    ); +    });      const r = await httpResp.text(); +    t.assertDeepEqual(httpResp.status, 402);      console.log(r); -    t.assertDeepEqual(httpResp.status, 404); -    // FIXME: also check Taler error code    } +  const preparePayResp = await wallet.client.call( +    WalletApiOperation.PreparePayForUri, +    { +      talerPayUri, +    }, +  ); + +  t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible); +  const contractTermsHash = preparePayResp.contractTermsHash; +  const proposalId = preparePayResp.proposalId; + +  // claimed, unpaid, access with wrong h_contract +  { +    const url = new URL(`orders/${orderId}`, merchantBaseUrl); +    const hcWrong = encodeCrock(getRandomBytes(64)); +    url.searchParams.set("h_contract", hcWrong); +    const httpResp = await httpLib.get(url.href); +    const r = await httpResp.json(); +    console.log(r); +    t.assertDeepEqual(httpResp.status, 403); +  } + +  // claimed, unpaid, access with wrong claim token +  { +    const url = new URL(`orders/${orderId}`, merchantBaseUrl); +    const ctWrong = encodeCrock(getRandomBytes(16)); +    url.searchParams.set("token", ctWrong); +    const httpResp = await httpLib.get(url.href); +    const r = await httpResp.json(); +    console.log(r); +    t.assertDeepEqual(httpResp.status, 403); +  } + +  // claimed, unpaid, access with correct claim token +  { +    const url = new URL(`orders/${orderId}`, merchantBaseUrl); +    url.searchParams.set("token", claimToken); +    const httpResp = await httpLib.get(url.href); +    const r = await httpResp.json(); +    console.log(r); +    t.assertDeepEqual(httpResp.status, 402); +  } + +  // claimed, unpaid, access with correct contract terms hash +  { +    const url = new URL(`orders/${orderId}`, merchantBaseUrl); +    url.searchParams.set("h_contract", contractTermsHash); +    const httpResp = await httpLib.get(url.href); +    const r = await httpResp.json(); +    console.log(r); +    t.assertDeepEqual(httpResp.status, 402); +  } + +  // claimed, unpaid, access without credentials +  { +    const url = new URL(`orders/${orderId}`, merchantBaseUrl); +    const httpResp = await httpLib.get(url.href); +    const r = await httpResp.json(); +    console.log(r); +    t.assertDeepEqual(httpResp.status, 202); +  } + +  const confirmPayRes = await wallet.client.call( +    WalletApiOperation.ConfirmPay, +    { +      proposalId: proposalId, +    }, +  ); + +  t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done); + +  // paid, access without credentials +  { +    const url = new URL(`orders/${orderId}`, merchantBaseUrl); +    const httpResp = await httpLib.get(url.href); +    const r = await httpResp.json(); +    console.log(r); +    t.assertDeepEqual(httpResp.status, 202); +  } + +  // paid, access with wrong h_contract +  { +    const url = new URL(`orders/${orderId}`, merchantBaseUrl); +    const hcWrong = encodeCrock(getRandomBytes(64)); +    url.searchParams.set("h_contract", hcWrong); +    const httpResp = await httpLib.get(url.href); +    const r = await httpResp.json(); +    console.log(r); +    t.assertDeepEqual(httpResp.status, 403); +  } + +  // paid, access with wrong claim token +  { +    const url = new URL(`orders/${orderId}`, merchantBaseUrl); +    const ctWrong = encodeCrock(getRandomBytes(16)); +    url.searchParams.set("token", ctWrong); +    const httpResp = await httpLib.get(url.href); +    const r = await httpResp.json(); +    console.log(r); +    t.assertDeepEqual(httpResp.status, 403); +  } + +  // paid, access with correct h_contract +  { +    const url = new URL(`orders/${orderId}`, merchantBaseUrl); +    url.searchParams.set("h_contract", contractTermsHash); +    const httpResp = await httpLib.get(url.href); +    const r = await httpResp.json(); +    console.log(r); +    t.assertDeepEqual(httpResp.status, 200); +  } + +  // paid, access with correct claim token, JSON +  { +    const url = new URL(`orders/${orderId}`, merchantBaseUrl); +    url.searchParams.set("token", claimToken); +    const httpResp = await httpLib.get(url.href); +    const r = await httpResp.json(); +    console.log(r); +    t.assertDeepEqual(httpResp.status, 200); +    const respFulfillmentUrl = r.fulfillment_url; +    t.assertDeepEqual(respFulfillmentUrl, "https://example.com/article42"); +  } + +  // paid, access with correct claim token, HTML +  { +    const url = new URL(`orders/${orderId}`, merchantBaseUrl); +    url.searchParams.set("token", claimToken); +    const httpResp = await httpLib.get(url.href, { +      headers: { Accept: "text/html" }, +    }); +    t.assertDeepEqual(httpResp.status, 200); +  } + +  const confirmPayRes2 = await wallet.client.call( +    WalletApiOperation.ConfirmPay, +    { +      proposalId: proposalId, +      sessionId: sessionId, +    }, +  ); + +  t.assertTrue(confirmPayRes2.type === ConfirmPayResultType.Done); + +  // Create another order with identical fulfillment URL to test the "already paid" flow +  const alreadyPaidOrderResp = await MerchantPrivateApi.createOrder( +    merchant, +    "default", +    { +      order: { +        summary: "Buy me!", +        amount: "TESTKUDOS:5", +        fulfillment_url: "https://example.com/article42", +        public_reorder_url: "https://example.com/article42-share", +      }, +    }, +  ); + +  const apOrderId = alreadyPaidOrderResp.order_id; +  const apToken = alreadyPaidOrderResp.token; +  t.assertTrue(!!apToken); + +  { +    const url = new URL(`orders/${apOrderId}`, merchantBaseUrl); +    url.searchParams.set("token", apToken); +    const httpResp = await httpLib.get(url.href); +    const r = await httpResp.json(); +    console.log(r); +    t.assertDeepEqual(httpResp.status, 402); +  } + +  // Check for already paid session ID, JSON +  { +    const url = new URL(`orders/${apOrderId}`, merchantBaseUrl); +    url.searchParams.set("token", apToken); +    url.searchParams.set("session_id", sessionId); +    const httpResp = await httpLib.get(url.href); +    const r = await httpResp.json(); +    console.log(r); +    t.assertDeepEqual(httpResp.status, 402); +    const alreadyPaidOrderId = r.already_paid_order_id; +    t.assertDeepEqual(alreadyPaidOrderId, orderId); +  } + +  // Check for already paid session ID, HTML +  { +    const url = new URL(`orders/${apOrderId}`, merchantBaseUrl); +    url.searchParams.set("token", apToken); +    url.searchParams.set("session_id", sessionId); +    const httpResp = await httpLib.get(url.href, { +      headers: { Accept: "text/html" }, +    }); +    t.assertDeepEqual(httpResp.status, 302); +    const location = httpResp.headers.get("Location"); +    console.log("location header:", location); +    t.assertDeepEqual(location, "https://example.com/article42"); +  } +} + +async function testWithoutClaimToken( +  t: GlobalTestState, +  c: Context, +): Promise<void> { +  const wallet = new WalletCli(t, "withoutct"); +  const sessionId = "mysession2"; +  const { bank, exchange } = c; +  const { merchant, merchantBaseUrl } = c; +  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });    const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {      order: {        summary: "Buy me!", @@ -102,11 +321,10 @@ export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {        fulfillment_url: "https://example.com/article42",        public_reorder_url: "https://example.com/article42-share",      }, +    create_token: false,    }); -  const claimToken = orderResp.token;    const orderId = orderResp.order_id; -  t.assertTrue(!!claimToken);    let talerPayUri: string;    { @@ -114,13 +332,12 @@ export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {        new URL(`orders/${orderId}`, merchantBaseUrl).href,      );      const r = await httpResp.json(); -    t.assertDeepEqual(httpResp.status, 202); +    t.assertDeepEqual(httpResp.status, 402);      console.log(r);    }    {      const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    url.searchParams.set("token", claimToken);      const httpResp = await httpLib.get(url.href);      const r = await httpResp.json();      t.assertDeepEqual(httpResp.status, 402); @@ -131,7 +348,6 @@ export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {    {      const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    url.searchParams.set("token", claimToken);      const httpResp = await httpLib.get(url.href, {        headers: {          Accept: "text/html", @@ -149,6 +365,8 @@ export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {      },    ); +  console.log(preparePayResp); +    t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible);    const contractTermsHash = preparePayResp.contractTermsHash;    const proposalId = preparePayResp.proposalId; @@ -175,10 +393,9 @@ export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {      t.assertDeepEqual(httpResp.status, 403);    } -  // claimed, unpaid, access with correct claim token +  // claimed, unpaid, no claim token    {      const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    url.searchParams.set("token", claimToken);      const httpResp = await httpLib.get(url.href);      const r = await httpResp.json();      console.log(r); @@ -201,7 +418,10 @@ export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {      const httpResp = await httpLib.get(url.href);      const r = await httpResp.json();      console.log(r); -    t.assertDeepEqual(httpResp.status, 202); +    // No credentials, but the order doesn't require a claim token. +    // This effectively means that the order ID is already considered +    // enough authentication, at least to check for the basic order status +    t.assertDeepEqual(httpResp.status, 402);    }    const confirmPayRes = await wallet.client.call( @@ -219,7 +439,7 @@ export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {      const httpResp = await httpLib.get(url.href);      const r = await httpResp.json();      console.log(r); -    t.assertDeepEqual(httpResp.status, 202); +    t.assertDeepEqual(httpResp.status, 200);    }    // paid, access with wrong h_contract @@ -254,10 +474,9 @@ export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {      t.assertDeepEqual(httpResp.status, 200);    } -  // paid, access with correct claim token, JSON +  // paid, JSON    {      const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    url.searchParams.set("token", claimToken);      const httpResp = await httpLib.get(url.href);      const r = await httpResp.json();      console.log(r); @@ -266,10 +485,9 @@ export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {      t.assertDeepEqual(respFulfillmentUrl, "https://example.com/article42");    } -  // paid, access with correct claim token, HTML +  // paid, HTML    {      const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    url.searchParams.set("token", claimToken);      const httpResp = await httpLib.get(url.href, {        headers: { Accept: "text/html" },      }); @@ -280,7 +498,7 @@ export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {      WalletApiOperation.ConfirmPay,      {        proposalId: proposalId, -      sessionId: "mysession", +      sessionId: sessionId,      },    ); @@ -317,7 +535,7 @@ export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {    {      const url = new URL(`orders/${apOrderId}`, merchantBaseUrl);      url.searchParams.set("token", apToken); -    url.searchParams.set("session_id", "mysession"); +    url.searchParams.set("session_id", sessionId);      const httpResp = await httpLib.get(url.href);      const r = await httpResp.json();      console.log(r); @@ -330,7 +548,7 @@ export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {    {      const url = new URL(`orders/${apOrderId}`, merchantBaseUrl);      url.searchParams.set("token", apToken); -    url.searchParams.set("session_id", "mysession"); +    url.searchParams.set("session_id", sessionId);      const httpResp = await httpLib.get(url.href, {        headers: { Accept: "text/html" },      }); @@ -341,4 +559,67 @@ export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {    }  } +/** + * Checks for the /orders/{id} endpoint of the merchant. + * + * The tests here should exercise all code paths in the executable + * specification of the endpoint. + */ +export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) { +  const { +    bank, +    exchange, +    merchant, +  } = await createSimpleTestkudosEnvironment(t); + +  // Base URL for the default instance. +  const merchantBaseUrl = merchant.makeInstanceBaseUrl(); + +  { +    const httpResp = await httpLib.get(new URL("config", merchantBaseUrl).href); +    const r = await httpResp.json(); +    console.log(r); +    t.assertDeepEqual(r.currency, "TESTKUDOS"); +  } + +  { +    const httpResp = await httpLib.get( +      new URL("orders/foo", merchantBaseUrl).href, +    ); +    const r = await httpResp.json(); +    console.log(r); +    t.assertDeepEqual(httpResp.status, 404); +    // FIXME: also check Taler error code +  } + +  { +    const httpResp = await httpLib.get( +      new URL("orders/foo", merchantBaseUrl).href, +      { +        headers: { +          Accept: "text/html", +        }, +      }, +    ); +    const r = await httpResp.text(); +    console.log(r); +    t.assertDeepEqual(httpResp.status, 404); +    // FIXME: also check Taler error code +  } + +  await testWithClaimToken(t, { +    merchant, +    merchantBaseUrl, +    exchange, +    bank, +  }); + +  await testWithoutClaimToken(t, { +    merchant, +    merchantBaseUrl, +    exchange, +    bank, +  }); +} +  runMerchantSpecPublicOrdersTest.suites = ["merchant"];  | 
