Explorar o código

Updated tests with new data and fixed issues in them. Added information on testing to README.md

slow_tiger hai 1 ano
pai
achega
b261da5da0

+ 8 - 0
README.md

@@ -178,3 +178,11 @@ GIT_WORK_TREE=/var/www/hardware.darkfi git checkout -f main
 - install nginx and copy sample file to `/etc/nginx/site-available/`; enable nginx site by making a symbolic link of it to `/etc/nginx/site-enabled/`: `sudo ln -s /etc/nginx/site-available/hardware.darkfi.nginx /etc/nginx/site-enabled/hardware.darkfi.nginx`
 - make new file `/etc/systemd/system/darkfi.service` and copy sample
 - `sudo systemctl enable darkfi`, `sudo systemctl start darkfi`
+
+## Tests
+
+Tests are using pytest and playwright for browser automation. To run the tests make sure that the test data is uploaded to your instance and the dev server is running:
+
+`pytest tests/` will run all the tests from the folder. To run specific tests you need to point pytest path to your test set.
+
+To test email functionality make sure to include MAIL_TO_TEST variable in .env. This will be an email that will be getting test messages.

+ 2 - 0
tests/pytest.ini

@@ -0,0 +1,2 @@
+[pytest]
+asyncio_default_fixture_loop_scope = function

+ 19 - 15
tests/test_checkout.py

@@ -1,12 +1,13 @@
 import os
 
+from playwright.sync_api import sync_playwright
 import pytest
 from asgi_csrf import asgi_csrf
 from fastapi.testclient import TestClient
 from lxml import html
 
 import re
-from playwright.sync_api import Page, expect
+from playwright.sync_api import Page
 
 from app.main import app
 from app.parser import read_file
@@ -18,8 +19,7 @@ def app_csrf():
     """Return a CSRF token."""
     return asgi_csrf(app, signing_secret=os.getenv("CSRF_SECRET_KEY"))
 
-
-def test_stripe_checkout(app_csrf, page: Page, capsys):
+def test_stripe_checkout(app_csrf, capsys):
     """This test simulates a Stripe-based checkout workflow:
     - first it POSTs a product to /checkout
     - then it triggers a checkout via POST to /stripe-checkout-session
@@ -32,7 +32,7 @@ def test_stripe_checkout(app_csrf, page: Page, capsys):
     settings = read_settings("settings.toml")
 
     # -- add a product
-    product_path = "products/cap-01"
+    product_path = "products/g-axxi-i"
     product_headers = {
         "Accept-Language": "en-US,en;q=0.5",
         "Referer": f"http://localhost:5014/{product_path}",
@@ -96,16 +96,22 @@ def test_stripe_checkout(app_csrf, page: Page, capsys):
     assert checkout_response_good.status_code == 303
 
     # stripe checkout page
-    page.goto(checkout_response_good.headers['location'])
+    with sync_playwright() as playwright:
+        browser = playwright.chromium.launch(headless=True)
+        context = browser.new_context()
+        page = context.new_page()
+
+        page.goto(checkout_response_good.headers['location'])
+        page.locator("#cardNumber").fill("4242 4242 4242 4242")
+        page.locator("#cardExpiry").fill("05/29")
+        page.locator("#cardCvc").fill("111")
+        page.locator("#billingName").fill(checkout_data_good['last_name'])
 
-    page.locator("#cardNumber").fill("4242 4242 4242 4242")
-    page.locator("#cardExpiry").fill("05/29")
-    page.locator("#cardCvc").fill("111")
-    page.locator("#billingName").fill(checkout_data_good['last_name'])
+        page.locator(".SubmitButton--complete").click()
+        page.goto("http://localhost:5014/checkout/success")
+        page.close()
+        browser.close()
 
-    page.locator(".SubmitButton--complete").click()
-    page.goto("http://localhost:5014/checkout/success")
-    
 
 def test_nowpayments_checkout(app_csrf):
     """This test simulates a NOWPayments-based checkout workflow:
@@ -120,7 +126,7 @@ def test_nowpayments_checkout(app_csrf):
     settings = read_settings("settings.toml")
 
     # -- add a product
-    product_path = "products/tshirt-hardware"
+    product_path = "products/g-axxi-i"
     product_headers = {
         "Accept-Language": "en-US,en;q=0.5",
         "Referer": f"http://localhost:5014/{product_path}",
@@ -144,8 +150,6 @@ def test_nowpayments_checkout(app_csrf):
         "product_path": product.meta.path,
         "price": product.meta.price,
         "quantity": 1,
-        "size": "s",
-        "style": "white",
         "csrftoken": product_csrf_token,
     }
 

+ 46 - 54
tests/test_e2e_checkout.py

@@ -1,56 +1,48 @@
-import re
-from playwright.sync_api import Playwright, sync_playwright, expect
+import pytest
+from playwright.async_api import async_playwright
 
+@pytest.mark.asyncio
+async def test_gui_NoPayment():
+    async with async_playwright() as playwright:
+        browser = await playwright.chromium.launch(headless=True)
+        context = await browser.new_context()
+        page= await context.new_page()
+        await page.goto("http://localhost:5014/")
+        await page.get_by_role("link", name="Agorism In The 21st Century -- II ").first.click()
+        await page.get_by_role("button", name="add to cart").click()
+        await page.wait_for_timeout(1000)
+        await page.get_by_role("link", name="The DarkFi eye logo.").click()
+        await page.get_by_role("link", name="Tshirt 01").click()
+        await page.get_by_role("group").locator("div").filter(has_text="s").nth(1).click()
+        await page.get_by_label("s", exact=True).check()
+        await page.get_by_role("button", name="add to cart").click()
+        await page.wait_for_timeout(1000)
+        await page.get_by_role("button", name="add to cart").click()
+        await page.wait_for_timeout(1000)
+        await page.get_by_label("Go to Checkout").click()
+        await page.locator("#price_1PTPnFKMYdpilqPdArvtszSN #button-plus").click()
+        await page.locator("#prod_RZBk650b1Tx95N").get_by_role("button").nth(1).click()
+        await page.get_by_placeholder("Email address").click()
+        await page.get_by_placeholder("Email address").fill("work@andrefincato.info")
+        await page.get_by_placeholder("Email address").press("Tab")
+        await page.get_by_placeholder("First name (optional)").fill("<script onerror='alert(\\\\'hax\\\\')'>Dark")
+        await page.get_by_placeholder("First name (optional)").press("Tab")
+        await page.get_by_placeholder("Last name").fill("Fi")
+        await page.get_by_placeholder("Last name").press("Tab")
+        await page.get_by_placeholder("Address", exact=True).fill("Somewhere 122")
+        await page.get_by_placeholder("Address", exact=True).press("Tab")
+        await page.get_by_placeholder("Address No.").fill("42")
+        await page.get_by_placeholder("Address No.").press("Tab")
+        await page.get_by_placeholder("Postal code").fill("1225AK")
+        await page.get_by_placeholder("Postal code").press("Tab")
+        await page.get_by_placeholder("City").fill("Amsterdam")
+        await page.get_by_label("Country").select_option("Netherlands")
+        await page.get_by_placeholder("Additional note (optional)").click()
+        await page.get_by_placeholder("Additional note (optional)").fill("eef+eifenf\\n\\neineienfeifne+eifenife\\neiufenf\\n\\n...")
+        await page.get_by_role("button", name="NOW Payments pay button").click()
+        await page.get_by_role("button", name="Next step").click()
+        await page.wait_for_timeout(90000)
+        assert "Thanks for your order." in await page.content()
 
-def run(playwright: Playwright) -> None:
-    browser = playwright.chromium.launch(headless=False)
-    context = browser.new_context()
-    page = context.new_page()
-    page.goto("http://localhost:5014/")
-    page.get_by_role("link", name="Cap 01 30CHF").click()
-    page.get_by_role("button", name="add to cart").click()
-    page.get_by_role("link", name="The DarkFi eye logo. A white").click()
-    page.get_by_role("link", name="Tshirt “hardware” 35CHF").click()
-    page.get_by_role("group").locator("div").filter(has_text="s").nth(1).click()
-    page.get_by_label("black").check()
-    page.get_by_label("s", exact=True).check()
-    page.get_by_role("button", name="add to cart").click()
-    page.get_by_role("button", name="add to cart").click()
-    page.get_by_label("Go to Checkout").click()
-    page.locator("#price_1PTPnFKMYdpilqPdArvtszSN #button-plus").click()
-    page.locator("#price_1PZCznKMYdpilqPduNMHsUiY").get_by_role("button").nth(1).click()
-    page.get_by_placeholder("Email address").click()
-    page.get_by_placeholder("Email address").fill("work@andrefincato.info")
-    page.get_by_placeholder("Email address").press("Tab")
-    page.get_by_placeholder("First name (optional)").fill("<script onerror='alert(\\\\'hax\\\\')'>Dark")
-    page.get_by_placeholder("First name (optional)").press("Tab")
-    page.get_by_placeholder("Last name").fill("Fi")
-    page.get_by_placeholder("Last name").press("Tab")
-    page.get_by_placeholder("Address", exact=True).fill("Somewhere 122")
-    page.get_by_placeholder("Address", exact=True).press("Tab")
-    page.get_by_placeholder("Apartment, suite, etc. (").press("Tab")
-    page.get_by_placeholder("Postal code").fill("1225AK")
-    page.get_by_placeholder("Postal code").press("Tab")
-    page.get_by_placeholder("City").fill("Amsterdam")
-    page.get_by_label("Country").select_option("Netherlands")
-    page.get_by_placeholder("Additional note (optional)").click()
-    page.get_by_placeholder("Additional note (optional)").fill("eef+eifenf\\n\\neineienfeifne+eifenife\\neiufenf\\n\\n...")
-    page.get_by_role("button", name="Stripe pay button").click()
-    page.get_by_placeholder("1234 1234 1234").click()
-    page.get_by_placeholder("1234 1234 1234").fill("4242 4242 4242 42422")
-    page.get_by_placeholder("1234 1234 1234").press("Tab")
-    page.get_by_placeholder("MM / YY").fill("05 / 29")
-    page.get_by_placeholder("MM / YY").press("Tab")
-    page.get_by_placeholder("CVC").fill("1111")
-    page.get_by_placeholder("CVC").press("Tab")
-    page.get_by_placeholder("Full name on card").fill("Fi")
-    page.get_by_test_id("hosted-payment-submit-button").click()
-    page.goto("http://localhost:5014/checkout/success")
-
-    # ---------------------
-    context.close()
-    browser.close()
-
-
-with sync_playwright() as playwright:
-    run(playwright)
+        await context.close()
+        await browser.close()

+ 1 - 1
tests/test_export_order_to_xlsx.py

@@ -2,7 +2,7 @@ import os
 
 from fastapi.testclient import TestClient
 
-from app.db import export_order_to_xlsx, get_product_by_product_id, fetch_order_product_list
+from app.db import export_order_to_xlsx, fetch_order_product_list
 from app.main import app
 from app.parser import read_file
 from app.read_settings import read_settings

+ 45 - 56
tests/test_product.py

@@ -11,7 +11,7 @@ from starsessions import (
 
 from app.main import app
 
-client = TestClient(app)
+client = TestClient(app, base_url="http://127.0.0.1:5014")
 
 
 @pytest.fixture
@@ -39,31 +39,19 @@ def prepare_request(product_id: str) -> tuple[dict[str, str], str]:
     tree = html.fromstring(response_get.content)
     csrf_token = tree.xpath("//input[@name='csrftoken']")[0].get("value")
 
-    return (headers, csrf_token)
+    return headers, csrf_token
 
 
-def test_add_product(capsys, app_csrf, session_store):
-    """Run tests for:
-    - adding a product with no options (size, style), good
-    - adding a product with no options, bad (wrong query, missing param)
-    - adding a product with options, good
-    - adding a product with options, bad (wrong options)
-
-    - removing a product with no options (size, style), good
-    - removing a product with no options, bad (wrong query, missing param)
-    - removing a product with options, good
-    - removing a product with options, bad (wrong options)
-
-    - remove a product entirely using the remove button
-    """
-    # -- product with no options (size, style)
-    headers_no_options, csrf_token_no_options = prepare_request("cap-01")
+def test_add_simple_product(capsys, app_csrf, session_store):
+    """Add simple product"""
+
+    headers_no_options, csrf_token_no_options = prepare_request("g-axxi-i")
 
     data_good_no_options_add = {
         "page": "product",
         "operation": "add",
-        "product_id": "stripe_product_abc",
-        "product_path": "products/cap-01",
+        "product_id": "price_1PTPnFKMYdpilqPdArvtszSN",
+        "product_path": "products/g-axxi-i",
         "price": 30,
         "quantity": 2,
         "csrftoken": csrf_token_no_options,
@@ -78,11 +66,16 @@ def test_add_product(capsys, app_csrf, session_store):
 
     assert response_good_no_options_add.status_code == 200
 
+def test_remove_simple_product(capsys, app_csrf, session_store):
+    """removing a product with no options (size, style), good """
+
+    headers_no_options, csrf_token_no_options = prepare_request("g-axxi-i")
+    
     data_good_no_options_remove = {
         "page": "product",
         "operation": "remove",
-        "product_id": "stripe_product_abc",
-        "product_path": "products/cap-01",
+        "product_id": "price_1PTPnFKMYdpilqPdArvtszSN",
+        "product_path": "products/g-axxi-i",
         "price": 30,
         "quantity": 1,
         "csrftoken": csrf_token_no_options,
@@ -94,14 +87,16 @@ def test_add_product(capsys, app_csrf, session_store):
 
     assert response_good_no_options_remove.status_code == 200
 
-    # wrong options
+def test_wrong_request_simple_product(capsys, app_csrf, session_store):
+    # Product operation with wrong query
+
+    headers_no_options, csrf_token_no_options = prepare_request("g-axxi-i")
+
     data_bad_no_options_wrong_add = {
         "page": "product",
-        "product_id": "stripe_product_abc",
-        "product_path": "products/cap-01",
+        "product_id": "price_1PTPnFKMYdpilqPdArvtszSN",
+        "product_path": "products/g-axxi-i",
         "price": 30,
-        "size": None,
-        "style": "black",
         "quantity": 2,
         "csrftoken": csrf_token_no_options,
     }
@@ -112,32 +107,15 @@ def test_add_product(capsys, app_csrf, session_store):
 
     assert response_bad_no_options_add.status_code == 422
 
-    data_bad_no_options_wrong_remove = {
-        "page": "product",
-        "product_id": "stripe_product_abc",
-        "product_path": "products/cap-01",
-        "price": 30,
-        "size": None,
-        "style": "black",
-        "quantity": 2,
-        "csrftoken": csrf_token_no_options,
-    }
-
-    response_bad_no_options_remove = client.post(
-        "/checkout", data=data_bad_no_options_wrong_remove, headers=headers_no_options
-    )
-
-    assert response_bad_no_options_remove.status_code == 422
-
-    # -- product with options (size, style)
-
-    headers_with_options, csrf_token_with_options = prepare_request("tshirt-darkfi")
+def test_add_product(capsys, app_csrf, session_store):
+    """ Adding a product with size/style """
+    headers_with_options, csrf_token_with_options = prepare_request("a-tshirt-01")
 
     data_good_with_options_add = {
         "page": "product",
         "operation": "add",
-        "product_id": "stripe_product_tshirt-df",
-        "product_path": "products/tshirt-darkfi",
+        "product_id": "prod_RZBk650b1Tx95N",
+        "product_path": "products/a-tshirt-01",
         "price": 50,
         "quantity": 3,
         "size": "m",
@@ -151,11 +129,15 @@ def test_add_product(capsys, app_csrf, session_store):
 
     assert response_good_with_options_add.status_code == 200
 
+def test_remove_product(capsys, app_csrf, session_store):
+    """ Removing product with size/style """
+    headers_with_options, csrf_token_with_options = prepare_request("a-tshirt-01")
+
     data_good_with_options_remove = {
         "page": "product",
         "operation": "remove",
-        "product_id": "stripe_product_tshirt-df",
-        "product_path": "products/tshirt-darkfi",
+        "product_id": "stripe_product_tshirt-aus",
+        "product_path": "products/a-tshirt-01",
         "price": 50,
         "quantity": 2,
         "size": "m",
@@ -169,12 +151,15 @@ def test_add_product(capsys, app_csrf, session_store):
 
     assert response_good_with_options_remove.status_code == 200
 
-    # wrong options
+def test_add_product_wrong_options(capsys, app_csrf, session_store):
+    """ Trying to add product with wrong size/style """
+    headers_with_options, csrf_token_with_options = prepare_request("a-tshirt-01")
+
     data_bad_with_options_wrong_add = {
         "page": "product",
         "operation": "add",
-        "product_id": "stripe_product_tshirt-df",
-        "product_path": "products/tshirt-darkfi",
+        "product_id": "prod_RZBk650b1Tx95N",
+        "product_path": "products/a-tshirt-01",
         "price": 50,
         "quantity": 5,
         "size": "x",
@@ -188,11 +173,15 @@ def test_add_product(capsys, app_csrf, session_store):
 
     assert response_bad_with_options_add.status_code == 422
 
+def test_remove_product_wrong_options(capsys, app_csrf, session_store):
+    """ Trying to remove product with wrong size/style """
+    headers_with_options, csrf_token_with_options = prepare_request("a-tshirt-01")
+
     data_bad_with_options_wrong_remove = {
         "page": "product",
         "operation": "remove",
-        "product_id": "stripe_product_tshirt-df",
-        "product_path": "products/tshirt-darkfi",
+        "product_id": "prod_RZBk650b1Tx95N",
+        "product_path": "products/a-tshirt-01",
         "price": 50,
         "quantity": 5,
         "size": "x",

+ 0 - 3
tests/test_shipping.py

@@ -3,11 +3,8 @@ import os
 import pytest
 from asgi_csrf import asgi_csrf
 from fastapi.testclient import TestClient
-from lxml import html
 
 from app.main import app
-from app.parser import read_file
-from app.read_settings import read_settings
 
 
 @pytest.fixture

+ 63 - 63
tests/test_update_product_inventory.py

@@ -1,10 +1,7 @@
 from fastapi.testclient import TestClient
 
-from app.db import (
-    fetch_order_product_list,
-    pick_last_order_id,
-    update_product_inventory,
-)
+from app.db import update_product_inventory
+
 from app.main import app
 from app.parser import read_file
 from app.read_settings import read_settings
@@ -12,12 +9,12 @@ from app.read_settings import read_settings
 client = TestClient(app)
 
 
-def get_inventory_amount(inventories, size: str, style: str) -> int:
+def get_inventory_amount(inventories, product_variation_id: str) -> int:
     """Helper function to get the inventory amount of the given product
-    with specified size and style.
+    with specified product_id
     """
     for inventory in inventories:
-        if inventory.size == size and inventory.style == style:
+        if inventory.product_variation_id == product_variation_id:
             return inventory.amount
 
 
@@ -30,61 +27,64 @@ def test_update_product_inventory():
     """
     settings = read_settings("settings.toml")
 
-    order_id = pick_last_order_id(settings.git_repo, settings.local_db.filepath)
+    product_list = {
+        "product_variation_id": "prod_RZBk650b1Tx95N__s",
+        "product_name": "a-tshirt-01",
+        "update_amount": 3,
+    }
+
+    tree = "products"
+
+    # -- store current inventory amount pre-update
+    pre_product = read_file(
+        settings.git_repo,
+        product_list["product_name"],
+        settings.document_match,
+        settings.block_types,
+        tree,
+    )
+
+    pre_inventory_amount = get_inventory_amount(
+        pre_product.meta.inventory,
+        product_list["product_variation_id"],
+    )
+
+    # -- update inventory
+    update_product_inventory(
+        product_list["product_name"],
+        product_list["update_amount"],
+        product_list["product_variation_id"],
+        settings,
+    )
+
+    # -- store updated inventory amount post-update
+    post_product = read_file(
+        settings.git_repo,
+        product_list["product_name"],
+        settings.document_match,
+        settings.block_types,
+        tree,
+    )
 
-    product_list = fetch_order_product_list(
-        order_id, settings.git_repo, settings.local_db.filepath
+    post_inventory_amount = get_inventory_amount(
+        post_product.meta.inventory,
+        product_list["product_variation_id"]
     )
 
-    if product_list:
-        for product_info in product_list:
-            tree = "products"
-
-            # -- store current inventory amount pre-update
-            pre_product = read_file(
-                settings.git_repo,
-                product_info.filename,
-                settings.document_match,
-                settings.block_types,
-                tree,
-            )
-
-            pre_inventory_amount = get_inventory_amount(
-                pre_product.meta.inventory,
-                product_info.size,
-                product_info.style,
-            )
-
-            # -- update inventory
-            update_product_inventory(
-                product_info.filename,
-                product_info.quantity,
-                product_info.size,
-                product_info.style,
-                settings,
-            )
-
-            # -- store updated inventory amount post-update
-            post_product = read_file(
-                settings.git_repo,
-                product_info.filename,
-                settings.document_match,
-                settings.block_types,
-                tree,
-            )
-
-            post_inventory_amount = get_inventory_amount(
-                post_product.meta.inventory,
-                product_info.size,
-                product_info.style,
-            )
-
-            # -- if pre_inventory_amount is already 0 make sure it
-            # stays like that also after the update else do subtraction
-
-            if pre_inventory_amount > 0:
-                assert (
-                    pre_inventory_amount - product_info.quantity
-                ) == post_inventory_amount
-            else:
-                assert pre_inventory_amount == post_inventory_amount
+    # -- if pre_inventory_amount is already 0 make sure it
+    # stays like that also after the update else do subtraction
+
+    if pre_inventory_amount > 0:
+        assert (
+            pre_inventory_amount - product_list["update_amount"]
+        ) == post_inventory_amount
+    else:
+        assert pre_inventory_amount == post_inventory_amount
+
+    # Restore the amount to original after the test
+    update_product_inventory(
+        product_list["product_name"],
+        -pre_inventory_amount,
+        product_list["product_variation_id"],
+        settings,
+    )