test_checkout.py 6.1 KB

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