ApiLogicServer 15.2.0__py3-none-any.whl → 15.2.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. api_logic_server_cli/api_logic_server.py +3 -2
  2. api_logic_server_cli/prototypes/base/.github/.copilot-instructions.md +115 -31
  3. api_logic_server_cli/prototypes/base/docs/training/testing.md +116 -18
  4. api_logic_server_cli/prototypes/base/test/api_logic_server_behave/behave_logic_report.py +55 -29
  5. api_logic_server_cli/prototypes/base/test/api_logic_server_behave/behave_logic_report.py.bak +285 -0
  6. api_logic_server_cli/prototypes/{base/.github/.copilot-instructionsZ.mdx → basic_demo/.github/.copilot-instructions.md} +111 -30
  7. api_logic_server_cli/prototypes/basic_demo/customizations/test/api_logic_server_behave/reports/Behave Logic Report Intro micro.md +35 -0
  8. api_logic_server_cli/prototypes/basic_demo/customizations/test/api_logic_server_behave/reports/Behave Logic Report Intro.md +35 -0
  9. api_logic_server_cli/prototypes/basic_demo/readme.md +12 -4
  10. api_logic_server_cli/prototypes/basic_demo/tutor.md +1196 -0
  11. api_logic_server_cli/prototypes/manager/.github/.copilot-instructions.md +50 -23
  12. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/.github/.copilot-instructions.md +3 -0
  13. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/docs/training/testing.md +305 -21
  14. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/behave_logic_report.py +13 -84
  15. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/behave_logic_report.py.bak +282 -0
  16. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/features/order_processing.feature +59 -50
  17. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/features/steps/order_processing_steps.py +395 -248
  18. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/behave.log +66 -62
  19. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Carbon_Neutral_Discount_A.log +51 -41
  20. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Change_Order_Customer.log +29 -0
  21. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Change_Product_in_Item.log +35 -0
  22. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Delete_Item_Reduces_Order.log +39 -19
  23. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Exceed_Credit_Limit_Rejec.log +36 -45
  24. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Good_Order_Placed_via_B2B.log +50 -40
  25. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Item_Quantity_Change.log +33 -0
  26. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Multi-Item_Order_via_B2B_.log +67 -0
  27. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Ship_Order_Excludes_from_.log +24 -14
  28. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Transaction_Processing.log +26 -17
  29. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Unship_Order_Includes_in_.log +24 -14
  30. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/reports/Behave Logic Report.md +361 -146
  31. api_logic_server_cli/prototypes/manager/system/ApiLogicServer-Internal-Dev/copilot-dev-context.md +275 -4
  32. api_logic_server_cli/prototypes/manager/system/app_model_editor/test/api_logic_server_behave/behave_logic_report.py +13 -75
  33. api_logic_server_cli/prototypes/manager/system/app_model_editor/test/api_logic_server_behave/behave_logic_report.py.bak +256 -0
  34. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_docs_logic/test/api_logic_server_behave/behave_logic_report.py +13 -75
  35. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_docs_logic/test/api_logic_server_behave/behave_logic_report.py.bak +256 -0
  36. api_logic_server_cli/prototypes/nw/test/api_logic_server_behave/reports/Behave Logic Report Intro micro.md +17 -0
  37. api_logic_server_cli/prototypes/nw/test/api_logic_server_behave/reports/Behave Logic Report Intro.md +17 -0
  38. {apilogicserver-15.2.0.dist-info → apilogicserver-15.2.7.dist-info}/METADATA +103 -23
  39. {apilogicserver-15.2.0.dist-info → apilogicserver-15.2.7.dist-info}/RECORD +43 -30
  40. {apilogicserver-15.2.0.dist-info → apilogicserver-15.2.7.dist-info}/WHEEL +0 -0
  41. {apilogicserver-15.2.0.dist-info → apilogicserver-15.2.7.dist-info}/entry_points.txt +0 -0
  42. {apilogicserver-15.2.0.dist-info → apilogicserver-15.2.7.dist-info}/licenses/LICENSE +0 -0
  43. {apilogicserver-15.2.0.dist-info → apilogicserver-15.2.7.dist-info}/top_level.txt +0 -0
@@ -1,20 +1,49 @@
1
+ ---
2
+ title: Copilot Instructions for Manager Workspace
3
+ Description: Manager-level instructions for creating projects
4
+ Source: ApiLogicServer-src/prototypes/manager/.github/.copilot-instructions.md
5
+ Propagation: BLT process → Manager workspace
6
+ Usage: AI assistants read this when user opens Manager workspace
7
+ version: 2.1
8
+ changelog:
9
+ - 2.1 (Oct 2025) - Added "What is the Project Manager?" orientation, friendly collaborative tone, conditional Quick Start for returning users
10
+ - 2.0 (Oct 2025) - OBX improvements, strengthen basic_demo as default path
11
+ - 1.0 (Initial) - Established project creation methods
12
+ ---
13
+
1
14
  # GitHub Copilot Instructions for GenAI-Logic (aka API Logic Server) - Project Manager
2
15
 
3
- ⚠️ **IMPORTANT FOR AI ASSISTANTS**: Always read and follow these instructions when helping users with this project.
16
+ ⚠️ **IMPORTANT FOR AI ASSISTANTS**: Always read and follow these instructions when helping with this project.
17
+
18
+ ## What is the Project Manager?
19
+
20
+ Welcome! This is the **Project Manager** - think of it as your workspace for creating and managing API Logic Server projects.
21
+
22
+ **What's here:**
23
+ - Sample databases you can use to create projects (`samples/dbs/`)
24
+ - Training materials and examples (`system/genai/examples/`)
25
+ - A shared virtual environment for all your projects (`venv/`)
26
+ - A place to create new projects (they'll appear as subdirectories)
4
27
 
5
- The Project Manager contains genai training, a virtual environment, and provides a convenient place to create projects.
28
+ **What you'll do here:**
29
+ Create projects from databases - each project becomes a complete, working microservice with API, Admin UI, and business logic support. Once created, you'll work inside that project to customize and extend it.
6
30
 
7
- > Note: your virtual environment is automatically configured in most cases; verify it with `genai-logic` - it it's not there, activate the `venv` in this project.
31
+ > Note: Your virtual environment is automatically configured in most cases; verify it with `genai-logic` - if it's not there, activate the `venv` in this project.
8
32
 
9
- ## Quick Start for New Users
33
+ ## Quick Start
10
34
 
11
- **ALWAYS start new users with the basic_demo** - it's the best introduction to the system.
35
+ **First time here?** Let's start with the 20-minute basic_demo tutorial - it's the best way to see what GenAI-Logic can do.
12
36
 
13
- It's a small sqlite database, and provides a readme that explores the project, and how to customize it:
37
+ Run this command:
14
38
 
15
39
  ```bash
16
40
  genai-logic create --project_name=basic_demo --db_url=basic_demo
17
41
  ```
42
+
43
+ After it runs, the project will auto-open with its own README and I'll guide you through a hands-on tour.
44
+
45
+ **Already have basic_demo?** Open it and say "Guide me through basic_demo" for the hands-on tour, or ask me what else we can work on.
46
+
18
47
  <br>
19
48
 
20
49
  ## Creating Projects
@@ -27,7 +56,7 @@ There are multiple ways to create projects (aka systems, microservices) - see th
27
56
 
28
57
  ### Method 1: Create Projects from an existing database (Infrastructure Only)
29
58
 
30
- If the user provides a database reference, use the command below. (Note: sqlite sample databases at `samples/dbs`).
59
+ If you have a database reference, I can create a project from it. (Sample databases are in `samples/dbs`).
31
60
 
32
61
  ```bash
33
62
  genai-logic create --project_name=nw --db_url=sqlite:///samples/dbs/nw.sqlite
@@ -35,13 +64,11 @@ genai-logic create --project_name=nw --db_url=sqlite:///samples/dbs/nw.sqlite
35
64
 
36
65
  **Important:** This creates the project structure, API, Admin App, and database models, but **NO business logic rules**.
37
66
 
38
- **To add logic:** Users must add rules separately using:
39
- - Natural language with Copilot/AI assistant in `logic/declare_logic.py`, or
40
- - Manual coding with IDE code completion
67
+ **To add logic:** We can add rules together using natural language in `logic/declare_logic.py`, or you can code them manually with IDE autocomplete.
41
68
 
42
69
  ### Method 2: Create Projects with GenAI (Complete System with Logic)
43
70
 
44
- **Use this when the user provides both database AND business logic requirements.**
71
+ **Use this when you have both database AND business logic requirements.**
45
72
 
46
73
  Describe database and logic in Natural Language in a prompt file.
47
74
 
@@ -58,25 +85,25 @@ genai-logic genai --using=system/genai/examples/genai_demo/genai_demo.prompt --p
58
85
  4. Creates test data
59
86
  5. Creates API, Admin App, and complete project structure
60
87
 
61
- **When to use:** User provides requirements like "Customer balance is sum of orders" or "Check credit limit" - the LLM will generate the corresponding `Rule.sum`, `Rule.constraint`, etc.
88
+ **When to use:** When you have requirements like "Customer balance is sum of orders" or "Check credit limit" - the LLM will generate the corresponding `Rule.sum`, `Rule.constraint`, etc.
62
89
 
63
90
  ### Method 3: Create Projects with new databases (Manual Approach)
64
91
 
65
- If the user provides a description but you want to create the database manually:
92
+ If you provide a description but want to create the database manually:
66
93
 
67
- 1. Create a sqlite database from the description. Be sure to include foreign keys. The system works well with `id INTEGER PRIMARY KEY AUTOINCREMENT`.
68
- 2. Then, use the `genai-logic create` command above.
69
- 3. If logic is provided, translate it using Copilot or `system/genai/learning_requests/logic_bank_api.prompt`.
94
+ 1. I'll create a sqlite database from your description. I'll be sure to include foreign keys. The system works well with `id INTEGER PRIMARY KEY AUTOINCREMENT`.
95
+ 2. Then, I'll use the `genai-logic create` command above.
96
+ 3. If you have logic requirements, we can translate them together using `system/genai/learning_requests/logic_bank_api.prompt`.
70
97
 
71
- ## AI Assistant Guidelines
98
+ ## Working Together
72
99
 
73
- When users ask "what do I do now?" or similar questions:
100
+ When you ask "what should I do now?" or similar:
74
101
 
75
- 1. **Always recommend starting with basic_demo** using the command above
76
- 2. **Use CLI commands** (genai-logic) not Docker scripts for project creation
77
- 3. **Refer to existing sample databases** in `samples/dbs/` for examples
78
- 4. **Point to GenAI examples** in `system/genai/examples/` for AI-generated projects
79
- 5. **Emphasize the 20-minute evaluation workflow** described in the main README
102
+ 1. **I'll recommend starting with basic_demo** using the command above
103
+ 2. **I'll use CLI commands** (genai-logic) not Docker scripts for project creation
104
+ 3. **I'll point you to sample databases** in `samples/dbs/` for examples
105
+ 4. **I'll show you GenAI examples** in `system/genai/examples/` for AI-generated projects
106
+ 5. **We'll follow the 20-minute workflow** described in the main README
80
107
 
81
108
  ## Available Databases
82
109
  - `basic_demo` - Best for first-time users
@@ -26,6 +26,9 @@ This is a **GenAI-Logic (aka API Logic Server) project** - a complete, working m
26
26
 
27
27
  3. **Automatic Business Logic**: All APIs (standard and custom) automatically inherit LogicBank rules without additional code.
28
28
 
29
+ 4. **CLI Commands**: Use `genai-logic --help` to see all available commands. When CLI commands exist for a task (e.g., `add-auth`, `genai-add-mcp-client`, `genai-add-app`), ALWAYS use them instead of manual configuration - they handle all setup correctly.
30
+
31
+
29
32
  ## ⚠️ IMPORTANT: What's Already Built
30
33
 
31
34
  **DO NOT recreate these - they're already working:**
@@ -19,11 +19,41 @@ Step 1c. Scan api/api_discovery/*.py → Discover custom APIs (PHASE 2!)
19
19
  Step 2. Decide Phase 1 vs Phase 2 → Based on custom API existence
20
20
  Step 3. Generate .feature files → Business language scenarios
21
21
  Step 4. Implement steps/*.py → Using discovered APIs or CRUD
22
- Step 5. Run testspython behave_run.py
22
+ Step 4b. VERIFY STEP ORDERING Multi-item BEFORE single-item (Rule #0.5!)
23
+ Step 5. SUGGEST how to run tests (DO NOT run automatically)
23
24
  ```
24
25
 
26
+ **CRITICAL PRE-TEST CHECKLIST:**
27
+ - [ ] Step 1c completed? (Custom APIs discovered)
28
+ - [ ] **Database values verified?** (Rule #10: Run SQL to check actual prices/flags)
29
+ - [ ] `sqlite3 db.sqlite "SELECT name, unit_price, carbon_neutral FROM product;"`
30
+ - [ ] Don't assume product attributes - verify BEFORE writing expectations!
31
+ - [ ] **Step ordering verified?** (Most specific → Most general)
32
+ - [ ] @when patterns: carbon neutral > multi-item > single-item
33
+ - [ ] @given patterns: multi-item > single-item
34
+ - [ ] Use `grep -n "@when('.*with" steps/*.py` to verify order
35
+ - [ ] **Clear scenario language?** (Rule #13: Action-oriented wording)
36
+ - [ ] Use "Order is created" not "Order exists" (shows when rules fire)
37
+ - [ ] Makes test flow obvious: setup → action → verify
38
+ - [ ] Test data uses timestamps? (Rule #0: Repeatability)
39
+ - [ ] Security config read? (SECURITY_ENABLED value)
40
+
25
41
  **DO NOT skip Step 1c!** Custom APIs change the entire testing approach.
26
42
 
43
+ **DO NOT skip Step 4b!** Wrong step ordering causes silent failures with balance=0.
44
+
45
+ **DO NOT run tests automatically!** Instead, suggest this workflow:
46
+
47
+ ```bash
48
+ # 1. Start the server (in separate terminal or background)
49
+ python api_logic_server_run.py
50
+
51
+ # 2. Run the tests (in another terminal)
52
+ python test/api_logic_server_behave/behave_run.py
53
+ ```
54
+
55
+ **Why manual execution?** Tests require a running server. The AI cannot manage multiple terminals or background processes reliably.
56
+
27
57
  ## Phase 1 vs Phase 2: The Core Decision
28
58
 
29
59
  ### Phase 1: CRUD-Level Testing
@@ -140,7 +170,7 @@ def step_impl(context, qty):
140
170
  r = requests.patch(url=patch_uri, json=patch_data)
141
171
  ```
142
172
 
143
- ## The 6 Critical Rules (Read FIRST!)
173
+ ## The Critical Rules (Read FIRST!)
144
174
 
145
175
  ### Rule #0: TEST REPEATABILITY (MOST CRITICAL!) ⚠️
146
176
 
@@ -213,7 +243,7 @@ def step_impl_general(context, customer_name, quantity, product_name):
213
243
  ...
214
244
  ```
215
245
 
216
- **Example 2: Multi-Item Orders**
246
+ **Example 2: Multi-Item Orders (GIVEN pattern)**
217
247
 
218
248
  ```python
219
249
  # ❌ WRONG ORDER - Single-item pattern matches "3 Widget and 2 Gadget"
@@ -242,6 +272,51 @@ def step_impl_single(context, customer_name, quantity, product_name):
242
272
  ...
243
273
  ```
244
274
 
275
+ **Example 3: Multi-Item Orders (WHEN pattern) - Real Bug Found!**
276
+
277
+ ```python
278
+ # ❌ WRONG ORDER - Causes silent failure with balance=0 instead of expected value
279
+ @when('B2B order placed for "{customer_name}" with {quantity:d} {product_name}')
280
+ def step_impl_single(context, customer_name, quantity, product_name):
281
+ # This matches "3 Widget and 2 Gadget" FIRST!
282
+ # product_name becomes "Widget and 2 Gadget" (entire string)
283
+ # OrderB2B API tries to find product "Widget and 2 Gadget" → fails
284
+ # Returns 200 but no order created → customer balance stays 0
285
+ # Test fails: "expected 570, got 0.0"
286
+ ...
287
+
288
+ @when('B2B order placed for "{customer_name}" with {qty1:d} {product1} and {qty2:d} {product2}')
289
+ def step_impl_multi(context, customer_name, qty1, product1, qty2, product2):
290
+ # NEVER REACHED! Single-item pattern matched first
291
+ ...
292
+
293
+ # ✅ CORRECT ORDER - Multi-item pattern MUST come before single-item
294
+ @when('B2B order placed for "{customer_name}" with {qty1:d} {product1} and {qty2:d} {product2}')
295
+ def step_impl_multi(context, customer_name, qty1, product1, qty2, product2):
296
+ # Now matches "3 Widget and 2 Gadget" correctly
297
+ # Creates order with 2 items, balance = 570
298
+ ...
299
+
300
+ @when('B2B order placed for "{customer_name}" with {quantity:d} {product_name}')
301
+ def step_impl_single(context, customer_name, quantity, product_name):
302
+ # Now only matches single product patterns
303
+ ...
304
+ ```
305
+
306
+ **CRITICAL FILE ORGANIZATION:**
307
+ Within each decorator type (@given, @when, @then), organize patterns like this:
308
+
309
+ ```python
310
+ # For @when patterns:
311
+ @when('... with {qty:d} carbon neutral {product}') # Most specific (3+ keywords)
312
+ @when('... with {qty1:d} {p1} and {qty2:d} {p2}') # More specific (multi-param + "and")
313
+ @when('... with {quantity:d} {product_name}') # General (fewest keywords)
314
+
315
+ # For @given patterns:
316
+ @given('... with {qty1:d} {p1} and {qty2:d} {p2}') # More specific (multi-param + "and")
317
+ @given('... with {quantity:d} {product_name}') # General (fewer params)
318
+ ```
319
+
245
320
  **Why This Matters:**
246
321
  - Wrong order → context.item_id not set → "Then Item amount" step fails with "item_id not set in context"
247
322
  - Behave doesn't warn about unreachable patterns
@@ -314,18 +389,65 @@ Scenario: Exceed Credit (Constraint FAIL - negative test)
314
389
 
315
390
  **CRITICAL:** Check `logic/declare_logic.py` for custom Python logic affecting calculations (e.g., discounts, adjustments) before writing constraint test expectations!
316
391
 
317
- ### Rule #5: Security Configuration
392
+ ### Rule #5: Security Configuration - JWT Authentication
393
+
394
+ **CRITICAL:** When `SECURITY_ENABLED=True`, tests MUST obtain JWT token and include Authorization header.
395
+
318
396
  ```python
319
- # Read config/default.env FIRST
320
- SECURITY_ENABLED = False # or True
397
+ from pathlib import Path
398
+ import os
399
+ from dotenv import load_dotenv
321
400
 
322
- # Match in test code
323
- if SECURITY_ENABLED == False:
324
- headers = {} # Empty
325
- else:
326
- headers = test_utils.login()
401
+ # Load config to check SECURITY_ENABLED
402
+ config_path = Path(__file__).parent.parent.parent.parent.parent / 'config' / 'default.env'
403
+ load_dotenv(config_path)
404
+
405
+ # Cache for auth token (obtained once per test session)
406
+ _auth_token = None
407
+
408
+ def get_auth_token():
409
+ """Login and get JWT token if security is enabled"""
410
+ global _auth_token
411
+
412
+ if _auth_token is not None:
413
+ return _auth_token
414
+
415
+ # Login with default admin credentials
416
+ login_url = f'{BASE_URL}/api/auth/login'
417
+ login_data = {'username': 'admin', 'password': 'p'}
418
+
419
+ response = requests.post(login_url, json=login_data)
420
+ if response.status_code == 200:
421
+ _auth_token = response.json().get('access_token')
422
+ return _auth_token
423
+ else:
424
+ raise Exception(f"Login failed: {response.status_code}")
425
+
426
+ def get_headers():
427
+ """Get headers including auth token if security is enabled"""
428
+ security_enabled = os.getenv('SECURITY_ENABLED', 'false').lower() not in ['false', 'no']
429
+
430
+ headers = {'Content-Type': 'application/json'}
431
+
432
+ if security_enabled:
433
+ token = get_auth_token()
434
+ if token:
435
+ headers['Authorization'] = f'Bearer {token}'
436
+
437
+ return headers
438
+
439
+ # Use in ALL API requests
440
+ response = requests.post(url=api_url, json=data, headers=get_headers())
441
+ response = requests.get(url=api_url, headers=get_headers())
442
+ response = requests.patch(url=api_url, json=data, headers=get_headers())
327
443
  ```
328
444
 
445
+ **Key Points:**
446
+ - Tests DO NOT automatically include auth - you must code the pattern above
447
+ - Token is cached globally to avoid repeated login calls
448
+ - Works for both `SECURITY_ENABLED=True` and `SECURITY_ENABLED=False`
449
+ - See complete example: `test/api_logic_server_behave/features/steps/order_processing_steps.py`
450
+
329
451
  ### Rule #6: Logic Logging
330
452
  ```python
331
453
  @when('Good Order Placed')
@@ -401,19 +523,28 @@ Rule.sum(derive=Customer.balance, as_sum_of=Order.amount_total,
401
523
  ```python
402
524
  # ALWAYS check actual product prices/flags before writing test expectations!
403
525
 
404
- # Check prices:
405
- # sqlite3 db.sqlite "SELECT name, unit_price, carbon_neutral FROM Product;"
526
+ # Check prices AND flags:
527
+ # sqlite3 db.sqlite "SELECT name, unit_price, carbon_neutral FROM product;"
406
528
 
407
- # Widget=90, Gadget=150, Green=109
529
+ # Results:
530
+ # 1|Gadget|150|1 ← carbon_neutral = 1 (TRUE)
531
+ # 2|Widget|90| ← carbon_neutral = NULL (not carbon neutral!)
532
+ # 3|Thingamajig|5075|
533
+ # 4|Doodad|110|
534
+ # 5|Green|109|1 ← carbon_neutral = 1 (TRUE)
408
535
 
409
- # ❌ WRONG - Assumed Widget=$100
410
- # Expected: 10 * 100 = 1000
536
+ # ❌ WRONG - Assumed Widget is carbon neutral
537
+ # Scenario: Carbon Neutral Discount
538
+ # When B2B order placed with 10 carbon neutral Widget
539
+ # Then balance should be 810 # Expected 10 * 90 * 0.9 = 810
540
+ # FAILS: Widget is NOT carbon neutral → no discount → balance = 900
411
541
 
412
- # ✅ CORRECT - Verified Widget=$90
413
- # Expected: 10 * 90 = 900
542
+ # ✅ CORRECT - Verified Gadget IS carbon neutral (flag = 1)
543
+ # Scenario: Carbon Neutral Discount
544
+ # When B2B order placed with 10 carbon neutral Gadget
545
+ # Then balance should be 1350 # Correct: 10 * 150 * 0.9 = 1350
414
546
 
415
- # For carbon neutral discount (10% off when qty >= 10):
416
- # Expected: 10 * 90 * 0.9 = 810
547
+ # CRITICAL: Don't assume product attributes - VERIFY with SQL first!
417
548
  ```
418
549
 
419
550
  ### Rule #11: Step Definitions Must Match Feature Files ⚠️ NEW
@@ -433,7 +564,7 @@ def step_impl(context, customer_name): # Missing balance and limit parameters!
433
564
  limit = 1000
434
565
  ```
435
566
 
436
- ### Rule #10: Always Initialize Context Variables ⚠️ NEW
567
+ ### Rule #12: Always Initialize Context Variables ⚠️ NEW
437
568
  ```python
438
569
  # ✅ CORRECT - prevents KeyError in subsequent steps
439
570
  @when('B2B order placed')
@@ -454,6 +585,40 @@ def step_impl(context):
454
585
  return # Skip gracefully
455
586
  ```
456
587
 
588
+ ### Rule #13: Use Clear, Action-Oriented Scenario Language ⚠️ NEW
589
+ ```gherkin
590
+ # ❌ AMBIGUOUS - "exists" is passive, doesn't show when balance changes
591
+ Scenario: Ship Order Excludes from Balance
592
+ Given Customer "Charlie" with balance 0 and credit limit 2000
593
+ And Order exists for "Charlie" with 2 Widget
594
+ When Order is shipped
595
+ Then Customer balance should be 0
596
+
597
+ # Question: How can Charlie have balance 0 AND an order for widgets?
598
+ # Answer: The order EXISTS at test start, but balance calculation is unclear
599
+
600
+ # ✅ CLEAR - "is created" shows action that triggers rule
601
+ Scenario: Ship Order Excludes from Balance
602
+ Given Customer "Charlie" with balance 0 and credit limit 2000
603
+ And Order is created for "Charlie" with 2 Widget # Action! Balance becomes 180
604
+ When Order is shipped # Action! Balance drops to 0
605
+ Then Customer balance should be 0 # Verification
606
+
607
+ # Now it's obvious:
608
+ # 1. Customer starts with balance 0
609
+ # 2. Creating unshipped order → rule fires → balance becomes 180
610
+ # 3. Shipping order → WHERE clause excludes it → balance drops to 0
611
+
612
+ # More examples:
613
+ # ❌ "And Shipped order exists for..."
614
+ # ✅ "And Shipped order is created for..."
615
+
616
+ # Why this matters:
617
+ # - Shows WHEN rules fire (on creation, not just existence)
618
+ # - Makes test flow obvious (setup → action → verify)
619
+ # - Clarifies that declarative rules execute automatically on changes
620
+ ```
621
+
457
622
  ## Complete Phase 2 Example
458
623
 
459
624
  ### .feature File
@@ -547,6 +712,45 @@ def step_impl(context, expected):
547
712
  | "row altered by another user" | Use direct FK: `"customer_id": int(id)` |
548
713
  | "circular import" | Remove imports from logic/, database/ |
549
714
  | "empty logic log" | Add `test_utils.prt(msg, scenario_name)` |
715
+ | **"balance: expected 570, got 0.0"** | **Step ordering issue (Rule #0.5)! Multi-item pattern after single-item** |
716
+ | **"context has no attribute 'item_id'"** | **Step ordering issue! Specific pattern defined after general pattern** |
717
+ | "Order created but no items" | Check if wrong step matched (print debug in step) |
718
+ | **"expected 810, got 900"** (carbon neutral) | **Rule #10: Verify actual database values! Check product flags with SQL first** |
719
+ | **"How can balance be 0 with an order?"** | **Rule #13: Use "is created" not "exists" - shows when rules fire** |
720
+
721
+ ### Debugging Step Ordering Issues
722
+
723
+ **Symptom**: Test passes WHEN step but THEN assertions fail with unexpected values (often 0 or None).
724
+
725
+ **Diagnosis**:
726
+ 1. **Check which step executed**: Look at test output for line numbers
727
+ ```
728
+ When B2B order placed for "Kevin" with 3 Widget and 2 Gadget # line=261
729
+ ```
730
+ If line 261 is single-item but you expected line 320 (multi-item), wrong pattern matched!
731
+
732
+ 2. **Verify database**: Check if records were actually created
733
+ ```bash
734
+ sqlite3 db.sqlite "SELECT * FROM 'order' WHERE customer_id = X;"
735
+ sqlite3 db.sqlite "SELECT * FROM item WHERE order_id = Y;"
736
+ ```
737
+
738
+ 3. **Check step file organization**: Count literal keywords in each pattern
739
+ ```python
740
+ # line 202: "carbon neutral" = 2 keywords (most specific)
741
+ # line 261: {quantity:d} {product_name} = 0 keywords (general)
742
+ # line 320: {qty1} {p1} "and" {qty2} {p2} = 1 keyword (more specific)
743
+ ```
744
+ Line 320 MUST come BEFORE line 261!
745
+
746
+ **Quick Fix**:
747
+ ```bash
748
+ # Find all @when patterns in your steps file
749
+ grep -n "@when('.*with.*{" features/steps/*.py
750
+
751
+ # Reorder so patterns with MORE keywords/parameters come FIRST
752
+ # Rule: Most specific → Most general (top to bottom)
753
+ ```
550
754
 
551
755
  ## Test Generation Workflow
552
756
 
@@ -591,3 +795,83 @@ def step_impl(context, expected):
591
795
 
592
796
  **The Magic:** Users built OrderB2B for partners → Same API provides natural test scenarios!
593
797
 
798
+ ## Automated Step Ordering Verification
799
+
800
+ **Command to verify step ordering in your test files:**
801
+
802
+ ```bash
803
+ # List all @when patterns with line numbers (should be ordered specific→general)
804
+ cd test/api_logic_server_behave
805
+ grep -n "@when('.*with.*{" features/steps/*.py
806
+
807
+ # Expected output (line numbers ascending = correct order):
808
+ # 202: @when('... with {qty:d} carbon neutral {product}') # Most specific
809
+ # 265: @when('... with {qty1:d} {p1} and {qty2:d} {p2}') # More specific
810
+ # 318: @when('... with {quantity:d} {product_name}') # General
811
+
812
+ # If multi-item (line 265) comes AFTER single-item (line 318) = WRONG!
813
+ ```
814
+
815
+ **Python script to auto-check ordering** (add to test directory):
816
+
817
+ ```python
818
+ #!/usr/bin/env python3
819
+ """Verify Behave step ordering for multi-param patterns."""
820
+ import re, sys
821
+ from pathlib import Path
822
+
823
+ def check_step_order(steps_file):
824
+ """Check if multi-item patterns come before single-item patterns."""
825
+ with open(steps_file) as f:
826
+ lines = f.readlines()
827
+
828
+ issues = []
829
+ when_patterns = []
830
+
831
+ for i, line in enumerate(lines, 1):
832
+ if match := re.match(r"@when\('.*with.*\{", line):
833
+ # Count parameters and keywords
834
+ param_count = line.count('{')
835
+ has_and = ' and ' in line
836
+ has_special_keyword = any(k in line for k in ['carbon neutral', 'shipped'])
837
+
838
+ specificity = param_count + (2 if has_and else 0) + (3 if has_special_keyword else 0)
839
+ when_patterns.append((i, specificity, line.strip()))
840
+
841
+ # Check if patterns are in descending specificity order
842
+ for i in range(len(when_patterns) - 1):
843
+ curr_line, curr_spec, curr_text = when_patterns[i]
844
+ next_line, next_spec, next_text = when_patterns[i + 1]
845
+
846
+ if next_spec > curr_spec:
847
+ issues.append(f"❌ Line {next_line} (specificity={next_spec}) should come BEFORE line {curr_line} (specificity={curr_spec})")
848
+ issues.append(f" More specific: {next_text}")
849
+ issues.append(f" Less specific: {curr_text}")
850
+
851
+ return issues
852
+
853
+ if __name__ == '__main__':
854
+ steps_dir = Path('features/steps')
855
+ all_issues = []
856
+
857
+ for steps_file in steps_dir.glob('*_steps.py'):
858
+ issues = check_step_order(steps_file)
859
+ if issues:
860
+ all_issues.extend([f"\n{steps_file}:"] + issues)
861
+
862
+ if all_issues:
863
+ print("Step Ordering Issues Found:")
864
+ print('\n'.join(all_issues))
865
+ sys.exit(1)
866
+ else:
867
+ print("✅ All step patterns correctly ordered (specific → general)")
868
+ sys.exit(0)
869
+ ```
870
+
871
+ **Usage:**
872
+ ```bash
873
+ cd test/api_logic_server_behave
874
+ python check_step_order.py # Run before committing tests
875
+ ```
876
+
877
+ This automation prevents Rule #0.5 violations across ALL databases and projects!
@@ -193,90 +193,19 @@ def main(behave_log: str, scenario_logs: str, wiki: str, prepend_wiki: str):
193
193
  wiki_data.append("&nbsp;")
194
194
  each_line = "## " + each_line
195
195
  if each_line.startswith(" Scenario"):
196
- # Before starting new scenario, show logic for previous one if we saw Then
197
- if just_saw_then and previous_scenario:
198
- show_logic(scenario=previous_scenario, logic_logs_dir=scenario_logs)
199
- just_saw_then = False
200
- each_line = tab + each_line
201
- if each_line.startswith(" Given") or \
202
- each_line.startswith(" When") or \
203
- each_line.startswith(" Then"):
204
- if each_line.startswith(" Then"):
205
- just_saw_then = True
206
- each_line = tab + tab + each_line
207
-
208
- each_line = each_line[:-1]
209
- debug_loc = each_line.find(behave_debug_info)
210
- if debug_loc > 0:
211
- each_line = each_line[0 : debug_loc]
212
- each_line = each_line.rstrip()
213
- if "Scenario" in each_line:
196
+ # Extract scenario name for logic lookup
214
197
  current_scenario = each_line[18:]
215
- previous_scenario = current_scenario
216
198
  wiki_data.append("&nbsp;")
217
199
  wiki_data.append("&nbsp;")
218
- wiki_data.append("### " + each_line[8:])
219
-
220
- each_line = each_line + " " # wiki for "new line"
221
-
222
- wiki_data.append(each_line)
223
-
224
- # Show logic for the last scenario if we saw Then
225
- if just_saw_then and current_scenario:
226
- show_logic(scenario=current_scenario, logic_logs_dir=scenario_logs)
227
-
228
- with open(wiki, 'w') as rpt:
229
- rpt.write('\n'.join(wiki_data))
230
- wiki_full_path = Path(wiki).absolute()
231
- print(f'Wiki Output: {wiki_full_path}\n\n')
232
-
233
-
234
-
235
- def print_args(args, msg):
236
- print(msg)
237
- for each_arg in args:
238
- print(f' {each_arg}')
239
- print(" ")
240
-
241
-
242
- @click.group()
243
- @click.pass_context
244
- def cli(ctx):
245
- """
246
- Combine behave.log and scenario_logic_logs to create Behave Logic Report
247
-
248
- """
249
- pass
250
-
251
-
252
- @cli.command("run")
253
- @click.pass_context
254
- @click.option('--behave_log',
255
- default=f'logs/behave.log', # cwd set to test/api_logic_server_behave
256
- # prompt="Log from behave test suite run [behave.log]",
257
- help="Help")
258
- @click.option('--scenario_logs',
259
- default=f'logs/scenario_logic_logs',
260
- # prompt="Logic Log directory from ",
261
- help="Help")
262
- @click.option('--wiki',
263
- default=f'reports/Behave Logic Report.md',
264
- # prompt="Log from behave test suite run [api_logic_server_behave]",
265
- help="Help")
266
- @click.option('--prepend_wiki',
267
- default=f'reports/Behave Logic Report Intro micro.md',
268
- # prompt="Log from behave test suite run [Behave Logic Report Intro]",
269
- help="Help")
270
- def run(ctx, behave_log: str, scenario_logs: str, wiki: str, prepend_wiki: str):
271
- main(behave_log = behave_log, scenario_logs = scenario_logs, wiki = wiki, prepend_wiki = prepend_wiki)
272
-
273
-
274
- if __name__ == '__main__': # debugger & python command line start here
275
- # eg: python api_logic_server_cli/cli.py create --project_name=~/Desktop/test_project
276
- # unix: python api_logic_server_cli/cli.py create --project_name=/home/ApiLogicProject
277
-
278
- print(f'\nBehave Logic Report 1.1, started at {os.getcwd()}')
279
- commands = sys.argv
280
- if len(sys.argv) > 1:
281
- print_args(commands, f'\n\nCommand Line Arguments:')
282
- cli()
200
+ # Remove the debug info (# features/...) from the scenario name
201
+ debug_loc = current_scenario.find(behave_debug_info)
202
+ if debug_loc > 0:
203
+ current_scenario = current_scenario[0:debug_loc].strip()
204
+ wiki_data.append("&nbsp;")
205
+ wiki_data.append("&nbsp;")
206
+ # Remove debug info from header line too
207
+ header_line = each_line[2:]
208
+ debug_loc = header_line.find(behave_debug_info)
209
+ if debug_loc > 0:
210
+ header_line = header_line[0:debug_loc].rstrip()
211
+ wiki_data.append("### " + header_line) # Add scenario header