# Business rules — katalon_tests

Evidence date: 2026-10-11.

Source: `git@bitbucket.org:overdrive/katalon_tests.git`, `main` at `7fa8653`.

These are rules of the test suite: how cases are chosen, how login and 2FA are judged, and how documents say failures should be handled. They are not confirmed business rules of the ShopFlow application. Where a script expects the application to behave a certain way, the rule is marked **Inferred** and means "the test expects this". It may be wrong about the product.

Do not copy passwords or mailbox secrets from `Profiles/default.glbl` into new documents. The committed password fields are empty. The Gmail address is stored in clear text in that file and is intentionally not repeated here.

## Rule status

| Label | Meaning |
|---|---|
| Documented | Stated in `README.md`, `TEST_COVERAGE.md`, `TAG_STRATEGY.md`, `DYNAMIC_TEST_SUITES.md`, or `ADD_TO_CART_TEST_FIXES.md` |
| Implemented | Enforced by a `.tc` tag, a suite filter, or a script branch |
| Inferred | A reasonable reading of a script that the source never states as a rule |
| Conflict | The document and the implementation disagree |

## Suite selection

### BR-001 — Smoke cases carry both P1 and SMOKE

**Documented** in `DYNAMIC_TEST_SUITES.md` and `TEST_COVERAGE.md`. **Implemented** as the filter text of `Smoke_Tests_Dynamic.ts`:

```text
tag=(P1)
tag=(SMOKE)
```

A case with only one of those tags is outside the smoke suite. Login's tags are `AUTH,P1,SMOKE,2FA`, so it is included even though it has no `UI` tag.

### BR-002 — Regression cases carry REGRESSION

**Documented** and **Implemented** as `tag=(REGRESSION)` on `Regression_Tests_Dynamic.ts`. Priority `P2` is not required by the filter. Every current regression case also has `P2`.

### BR-003 — P1 failure stops the run; other priorities continue

**Documented** in `TAG_STRATEGY.md`: P1 stops on first failure; P2, P3, and P4 continue. Smoke timeout 30 seconds and one retry; regression timeout up to 300 seconds and two or three retries.

**Implemented** in part. The smoke entity sets `numberOfRerun` 1 and page-load timeout 30. The regression entity sets `numberOfRerun` 2 and page-load timeout 60. `TagBasedExecution.groovy` would set 60s/1, 120s/2, and 300s/3 from tags, but `findTestCasesByTags` returns no cases, so that method does not run the suite.

**Conflict.** Individual steps often use `FailureHandling.OPTIONAL` or `KeywordUtil.markFailed`. An OPTIONAL miss does not stop the case. The document's "stop the suite" rule is not a single flag on the smoke entity.

### BR-004 — Environment and module tags are mandatory

**Documented** in `TAG_STRATEGY.md`: every case has a business-module tag and a priority tag, in uppercase with underscores.

**Implemented** for the 22 tagged cases. **Conflict** for `Read GMail 2fa Code` and `placeholder`, which have empty `<tag>` elements. No case uses `DEV`, `QA`, `STAGING`, `PROD`, `DATABASE`, or `PERFORMANCE`. The reports case uses `SHOPFLOW_REPORTS` rather than the documented module tag `REPORTS`.

### BR-005 — New cases join a suite by tag alone

**Documented** in `DYNAMIC_TEST_SUITES.md`: adding `P1,SMOKE` or `REGRESSION` on the `.tc` file is enough.

**Implemented** as the filtering entities. The shell scripts do not pass `-includeTags`. `TAG_STRATEGY.md` examples that pass `-includeTags` against `All_Tests` and `API_Tests` have no suite files.

## Authentication

### BR-006 — One profile holds the site and the mailbox

**Documented** in `README.md`: IMAP credentials, user credentials, and the site URL are in `Profiles/default`.

**Implemented.** `url` is `http://shopflow.io.dedi1534.jnb2.host-h.net`. `imap_host` is `imap.gmail.com`. `imap_port` is `993`. `username` and `imap_user` are the same Gmail address. `password` and `imap_pass` are protected and empty in git.

**Inferred.** A run that does not override the protected fields cannot complete a real login or a real IMAP read.

### BR-007 — Login requires the email field, the password field, and the login button

**Implemented.** `Login` calls `WebUI.fail` when any of `input_LOGIN_inputEmail`, `input_LOGIN_inputPassword`, or `button_Forgot password_btn btn-primary` is absent, or when the window title is empty.

Remember me (`checkbox_Remember me`) and forgot password (`link_Forgot password`) are optional. Missing them does not fail the case. **Inferred:** the suite does not require those controls.

### BR-008 — Submitted email must match the profile

**Implemented.** After `setText`, Login reads the email attribute and fails if it is not `GlobalVariable.username`. The password attribute is compared the same way, and a mismatch fails without printing the password.

### BR-009 — 2FA is mandatory only when the verification field is on the page

**Implemented.**

- If `input_Remember this device for 30 days_inpu_99772f` is absent, Login logs that it is continuing without 2FA.
- If the field is present, Login calls `Read GMail 2fa Code` and fails when the returned code is empty or the call throws.
- The reader searches INBOX for subject `2FA Validation` for 30 seconds, every 5 seconds.
- It prefers a message sent after `GlobalVariable.LoginDate` (`yyyy/MM/dd HH:mm:ss` captured just before the login click). If none is newer, it uses the newest message with that subject.
- The code is the trimmed text after `Your 2FA code is:` on a line of the plain-text body.
- IMAP SSL trust is `*` and server-identity checks are off. **Implemented.** This accepts any certificate presented by `imap.gmail.com`.

The 2FA case has no tags of its own. **Inferred:** it is a subroutine, not a smoke case.

### BR-010 — Login success is a changed title and a changed URL

**Implemented.** Login treats the attempt as successful when `getWindowTitle()` and `getUrl()` both differ from the values taken before submit. Dashboard heading, home heading, welcome text, logout button, and profile link are optional detections. Absence does not by itself fail this check.

**Inferred.** A redirect to an error page with a different title would satisfy this condition. **Requires Confirmation** if that is acceptable.

### BR-011 — Other cases should include Login

**Documented** in `README.md`.

**Conflict.** `callTestCase` of `Test Cases/01_Authentication/Login` appears in Logout (several times, with mixed `CONTINUE_ON_FAILURE` and one `STOP_ON_FAILURE`) and once in Order_Management (`STOP_ON_FAILURE`). Add_To_Cart, Dashboard, browse, cart, admin, pricing, and reports scripts do not call it. Their comment or the suite setup is expected to have logged in. Suite `@SetUp` opens a browser and `@SetupTestCase` opens `GlobalVariable.url`. Neither calls Login.

Login itself calls `openBrowser` again. **Inferred:** a smoke run can open two browsers, one in suite setup and one inside Login.

### BR-012 — Invalid login stays on the form and shows an error

**Documented** in the Invalid_Login header and in `TEST_COVERAGE.md`: empty credentials, bad email format, wrong password, unknown user, SQL-injection-shaped input, and XSS-shaped input.

**Implemented** as submissions followed by searches for words such as `required`, `empty`, and `error` in the page source, plus `KeywordUtil.markFailed` when the search fails. The script does not open a browser.

**Inferred**, and not a confirmed application rule: the test expects the login page to reject those strings and remain on the form. It does not prove that the server rejects them.

The header mentions account lockout. No lockout threshold or lockout message string was identified as a hard assertion in the portion reviewed. **Unknown / Requires Confirmation** whether a lockout rule is asserted later in that file.

## Commerce and admin expectations (inferred from navigation)

These paths are what the scripts open. They are **Inferred** routes of the system under test, not a confirmed route table.

| Concern | Rule the script acts on | Label |
|---|---|---|
| Shop | Add to cart and catalog browse use `/shop` | **Inferred** |
| Products table | Element id `productsTable` | **Inferred**, and **Documented** as the stable locator in `ADD_TO_CART_TEST_FIXES.md` |
| Add control | Button `onclick` contains `addProductToTable` and may carry `data-product` JSON | **Documented** in the add-to-cart note; **Implemented** as the locator strategy that note describes |
| Quantity | Input `input[type="text"][data-quantity]` inside a quantity control | **Documented** |
| Cart | `/carts`, table id `cartsTable` | **Documented** for the table id |
| Orders | `/orders` | **Inferred** |
| Home / dashboard | Dashboard case uses `/home`. Order management also opens `/dashboard` | **Inferred**. Two paths are used for overview screens |
| Logout | `/Welcome/logout` | **Inferred**. The `Welcome` segment looks like a CodeIgniter controller name |
| Profile | `/users/userProfile` | **Inferred** |
| Sellers | `/seller-management` | **Inferred** |
| Stores | `/storemanagement` | **Inferred** |
| Seller groups | `/sellergroups` | **Inferred** |
| Buying groups | `/admin/buying-groups` | **Inferred** |
| Companies | `/companymanagement` | **Inferred** |
| Company users | `/admin/company-users` | **Inferred** |
| Price files | Upload UI at `/productmanagement/pricefiles`. Management UI at `/pricefiles`. The placeholder script also uses `/pricefiles/list` | **Inferred**. Two management paths are in different scripts |
| Pricing rules | `/pricefilemappingrules` | **Inferred** |
| Product admin | `/productmanagement` | **Inferred** |
| Reports | `/reports` | **Inferred** |

### BR-013 — Do not bind locators to styling classes

**Documented** in `ADD_TO_CART_TEST_FIXES.md`: do not use classes such as `padding-0`, `btn-success`, `text-end`, `navbar`, or `breadcrumb` as the primary selector. Prefer `id`, `data-*`, `onclick`, `aria-*`, and table position. Semantic classes such as `description-main` are fallbacks only.

**Implemented** for `productsTable` (`#productsTable`, selector method CSS). The project default selector method is still XPATH. The rule was not verified against all 528 objects. `Page_AdminOverdrive` object names include sorting-class fragments such as `text-center sorting`, which conflicts with the written rule for those older objects.

### BR-014 — A displayed price is a non-negative number, with optional currency symbols

**Implemented** in `Add_To_Cart` function `isValidPriceFormat`. It strips `$`, `R`, and commas, parses a double, and accepts values `>= 0`. This is a test helper, not an application pricing rule.

### BR-015 — Price-file fixtures have a fixed column set

**Documented** by the files in `TestData/`, and described in `TEST_COVERAGE.md`.

**Implemented** as file contents only. Columns are `Product_ID`, `Product_Name`, `Category`, `Price`, `Currency`, `Effective_Date`. The invalid file uses a negative price, a non-numeric price, an empty name, an invalid date, and currency `INVALID_CURRENCY`. `invalid_file.txt` is prose, not CSV.

**Conflict.** No script uploads these files. `Price_File_Upload` checks a displayed file name against the extensions `csv`, `xlsx`, `xls`, and `txt`. That extension check would accept `invalid_file.txt` if the UI showed that name. **Inferred** from the script, and it disagrees with the text file's own statement that the format should be rejected.

### BR-016 — Regression setup creates and deletes users, products, and orders

**Documented** by the function names in `Regression_Tests_Dynamic.groovy`.

**Conflict.** `createTestUsers`, `createTestProducts`, `createTestOrders`, `removeTestUsers`, `removeTestProducts`, and `removeTestOrders` only print. No application data is created or removed.

## API

### BR-017 — The packaged suites do not run the API case

**Implemented.** Tags `INTEGRATION,P4,PLACEHOLDER,API` match neither dynamic filter.

**Conflict.** `TEST_COVERAGE.md` says API test-type coverage is 100% and integration coverage is 10%. The script header says the case is P1 and covers all endpoints.

### BR-018 — Organisation calls use an example identity, and HTTP 401 fails the case

**Implemented.** `POST /organisations/login` sends a hard-coded example username and password, not `GlobalVariable.username`. Status 200 is success only when the body contains `token` or `access_token`. Status 401 on later organisation calls is sent to `KeywordUtil.markFailed` even though the message says the 401 is expected.

**Inferred paths, not confirmed routes:** `/organisations`, `/organisations/{id}`, `/store-api`, `/api-simulation`, `/invalid-endpoint`. The `api-simulation` path is a sign that this block may target a stub. **Requires Confirmation.**

## Execution and reporting

### BR-019 — Chrome is the browser, started maximised, with certificate errors ignored

**Implemented** in the shell scripts, the suite collection, and `com.kms.katalon.core.webui.chrome.properties` (`start-maximized`, `ignore-certificate-errors`, `disable-popup-blocking`, `auth-server-whitelist`).

**Documented** Firefox and Safari in `TEST_COVERAGE.md` are not configured.

### BR-020 — Suite summaries count passes and failures

**Conflict.** Counters are reset to 0 and printed. Nothing in the Groovy sources assigns `smokeTestPassCount` or `regressionTestPassCount` except that reset. A summary after a real run would still show zeros.

### BR-021 — P1 fixes block progress; P2 fixes are due within 24 hours; P3/P4 within the sprint

**Documented** in `TAG_STRATEGY.md`. No tracker, mail recipient, or pipeline enforces it. Suite entities have an empty `<mailRecipient>`.

### BR-022 — Coverage percentages are not a gate

**Documented** figures in `TEST_COVERAGE.md` (99% functional, 100% by priority and by test type) are not produced by a tool in this repository. They are not an implemented gate.

## Conflicts to confirm

1. README says every test includes Login. Most scripts do not.
2. Documented case count 28 versus 24 `.tc` files versus module arithmetic of 22.
3. Documented P1 count 10 and P2 count 17 versus tags P1 = 7 and P2 = 14.
4. `REPORTS` versus `SHOPFLOW_REPORTS`.
5. API case is a placeholder in the `.tc` file and a live REST script in Groovy.
6. Price fixtures are documented and unused. The upload script's extension check accepts `.txt`.
7. Tag discovery helper returns no cases, while the dynamic `.ts` filters are the real mechanism.
8. Add-to-cart locator policy versus `Page_AdminOverdrive` objects whose names embed styling classes.
9. Sonar files listed when the onboarding issue was created are not in `7fa8653`.
