test_checkout.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import os
  2. import pytest
  3. from asgi_csrf import asgi_csrf
  4. from fastapi.testclient import TestClient
  5. from lxml import html
  6. import re
  7. from playwright.sync_api import Page, expect
  8. from app.main import app
  9. from app.parser import read_file
  10. from app.read_settings import read_settings
  11. @pytest.fixture
  12. def app_csrf():
  13. """Return a CSRF token."""
  14. return asgi_csrf(app, signing_secret=os.getenv("CSRF_SECRET_KEY"))
  15. def test_stripe_checkout(app_csrf, page: Page, capsys):
  16. """This test simulates a Stripe-based checkout workflow:
  17. - first it POSTs a product to /checkout
  18. - then it triggers a checkout via POST to /stripe-checkout-session
  19. """
  20. client = TestClient(
  21. app,
  22. base_url="http://127.0.0.1:5014",
  23. )
  24. settings = read_settings("settings.toml")
  25. # -- add a product
  26. product_path = "products/cap-01"
  27. product_headers = {
  28. "Accept-Language": "en-US,en;q=0.5",
  29. "Referer": f"http://localhost:5014/{product_path}",
  30. }
  31. product_response = client.get(f"/{product_path}", headers=product_headers)
  32. product_tree = html.fromstring(product_response.content)
  33. product_csrf_token = product_tree.xpath("//input[@name='csrftoken']")[0].get(
  34. "value"
  35. )
  36. product = read_file(
  37. settings.git_repo, product_path, settings.document_match, settings.block_types
  38. )
  39. product_data = {
  40. "page": "checkout",
  41. "operation": "add",
  42. "product_id": product.meta.product_id,
  43. "product_path": product.meta.path,
  44. "price": product.meta.price,
  45. "quantity": 1,
  46. "csrftoken": product_csrf_token,
  47. }
  48. client.post("/checkout", data=product_data, headers=product_headers)
  49. # -- make checkout order
  50. checkout_response_get = client.get("/checkout")
  51. checkout_tree = html.fromstring(checkout_response_get.content)
  52. # NOTE if order_id returns index out of range, it means the checkout page has no product-item in it
  53. # eg. the test product we use could have its inventory amount set to 0.
  54. order_id = checkout_tree.xpath("//input[@name='order_id']")[0].get("value")
  55. csrf_token = checkout_tree.xpath("//input[@name='csrftoken']")[0].get("value")
  56. checkout_data_good = {
  57. "csrftoken": csrf_token,
  58. "order_id": order_id,
  59. "weight": product.meta.weight,
  60. "email": "test@dark.fi",
  61. "first_name": "Dark",
  62. "last_name": "Fi",
  63. "address": "Somewhere",
  64. "address_no": "12b",
  65. "address_extra": "I belong",
  66. "postal_code": "1225AK",
  67. "city": "Amsterdam",
  68. "country": "Netherlands",
  69. "phone_number": "+310628551809",
  70. "note": "",
  71. }
  72. # NOTE: add `allow_redirect=False` to avoid FastAPI returning 404
  73. # instead of actual HTTP response, see
  74. # <https://github.com/tiangolo/fastapi/issues/790>.
  75. checkout_response_good = client.post(
  76. "/stripe-checkout-session", data=checkout_data_good, follow_redirects=False
  77. )
  78. assert checkout_response_good.status_code == 303
  79. # stripe checkout page
  80. page.goto(checkout_response_good.headers['location'])
  81. page.locator("#cardNumber").fill("4242 4242 4242 4242")
  82. page.locator("#cardExpiry").fill("05/29")
  83. page.locator("#cardCvc").fill("111")
  84. page.locator("#billingName").fill(checkout_data_good['last_name'])
  85. page.locator(".SubmitButton--complete").click()
  86. page.goto("http://localhost:5014/checkout/success")
  87. def test_nowpayments_checkout(app_csrf):
  88. """This test simulates a NOWPayments-based checkout workflow:
  89. - first it POSTs a product to /checkout
  90. - then it triggers a checkout via POST to /create-now-payments
  91. """
  92. client = TestClient(
  93. app,
  94. base_url="http://127.0.0.1:5014",
  95. )
  96. settings = read_settings("settings.toml")
  97. # -- add a product
  98. product_path = "products/tshirt-hardware"
  99. product_headers = {
  100. "Accept-Language": "en-US,en;q=0.5",
  101. "Referer": f"http://localhost:5014/{product_path}",
  102. }
  103. product_response = client.get(f"/{product_path}", headers=product_headers)
  104. product_tree = html.fromstring(product_response.content)
  105. product_csrf_token = product_tree.xpath("//input[@name='csrftoken']")[0].get(
  106. "value"
  107. )
  108. product = read_file(
  109. settings.git_repo, product_path, settings.document_match, settings.block_types
  110. )
  111. product_data = {
  112. "page": "checkout",
  113. "redirect_with_items": True,
  114. "operation": "add",
  115. "product_id": product.meta.product_id,
  116. "product_path": product.meta.path,
  117. "price": product.meta.price,
  118. "quantity": 1,
  119. "size": "s",
  120. "style": "white",
  121. "csrftoken": product_csrf_token,
  122. }
  123. client.post("/checkout", data=product_data, headers=product_headers)
  124. # -- make checkout order
  125. checkout_response_get = client.get("/checkout")
  126. checkout_tree = html.fromstring(checkout_response_get.content)
  127. order_id = checkout_tree.xpath("//input[@name='order_id']")[0].get("value")
  128. csrf_token = checkout_tree.xpath("//input[@name='csrftoken']")[0].get("value")
  129. checkout_data_good = {
  130. "csrftoken": csrf_token,
  131. "order_id": order_id,
  132. "weight": product.meta.weight,
  133. "email": "test@dark.fi",
  134. "first_name": "<script onerror='alert(\\'hax\\')'>Dark",
  135. "last_name": "Fi",
  136. "address": "Somewhere",
  137. "address_no": "12b",
  138. "address_extra": "I belong",
  139. "postal_code": "1225AK",
  140. "city": "Amsterdam",
  141. "country": "Netherlands",
  142. "phone_number": "",
  143. "note": "eef+eifenf\n\neineienfeifne+eifenife\neiufenf\n\n...",
  144. }
  145. # NOTE: add `allow_redirect=False` to avoid FastAPI returning 404
  146. # instead of actual HTTP response, see
  147. # <https://github.com/tiangolo/fastapi/issues/790>.
  148. checkout_response_good = client.post(
  149. "/create-now-payments", data=checkout_data_good, follow_redirects=False
  150. )
  151. assert checkout_response_good.status_code == 303