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.
- api_logic_server_cli/api_logic_server.py +3 -2
- api_logic_server_cli/prototypes/base/.github/.copilot-instructions.md +115 -31
- api_logic_server_cli/prototypes/base/docs/training/testing.md +116 -18
- api_logic_server_cli/prototypes/base/test/api_logic_server_behave/behave_logic_report.py +55 -29
- api_logic_server_cli/prototypes/base/test/api_logic_server_behave/behave_logic_report.py.bak +285 -0
- api_logic_server_cli/prototypes/{base/.github/.copilot-instructionsZ.mdx → basic_demo/.github/.copilot-instructions.md} +111 -30
- api_logic_server_cli/prototypes/basic_demo/customizations/test/api_logic_server_behave/reports/Behave Logic Report Intro micro.md +35 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/test/api_logic_server_behave/reports/Behave Logic Report Intro.md +35 -0
- api_logic_server_cli/prototypes/basic_demo/readme.md +12 -4
- api_logic_server_cli/prototypes/basic_demo/tutor.md +1196 -0
- api_logic_server_cli/prototypes/manager/.github/.copilot-instructions.md +50 -23
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/.github/.copilot-instructions.md +3 -0
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/docs/training/testing.md +305 -21
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/behave_logic_report.py +13 -84
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/behave_logic_report.py.bak +282 -0
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/features/order_processing.feature +59 -50
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/features/steps/order_processing_steps.py +395 -248
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/behave.log +66 -62
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Transaction_Processing.log +26 -17
- 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
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/reports/Behave Logic Report.md +361 -146
- api_logic_server_cli/prototypes/manager/system/ApiLogicServer-Internal-Dev/copilot-dev-context.md +275 -4
- api_logic_server_cli/prototypes/manager/system/app_model_editor/test/api_logic_server_behave/behave_logic_report.py +13 -75
- api_logic_server_cli/prototypes/manager/system/app_model_editor/test/api_logic_server_behave/behave_logic_report.py.bak +256 -0
- 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
- 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
- api_logic_server_cli/prototypes/nw/test/api_logic_server_behave/reports/Behave Logic Report Intro micro.md +17 -0
- api_logic_server_cli/prototypes/nw/test/api_logic_server_behave/reports/Behave Logic Report Intro.md +17 -0
- {apilogicserver-15.2.0.dist-info → apilogicserver-15.2.7.dist-info}/METADATA +103 -23
- {apilogicserver-15.2.0.dist-info → apilogicserver-15.2.7.dist-info}/RECORD +43 -30
- {apilogicserver-15.2.0.dist-info → apilogicserver-15.2.7.dist-info}/WHEEL +0 -0
- {apilogicserver-15.2.0.dist-info → apilogicserver-15.2.7.dist-info}/entry_points.txt +0 -0
- {apilogicserver-15.2.0.dist-info → apilogicserver-15.2.7.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
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
|
-
|
|
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:
|
|
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
|
|
33
|
+
## Quick Start
|
|
10
34
|
|
|
11
|
-
**
|
|
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
|
-
|
|
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
|
|
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:**
|
|
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
|
|
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:**
|
|
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
|
|
92
|
+
If you provide a description but want to create the database manually:
|
|
66
93
|
|
|
67
|
-
1.
|
|
68
|
-
2. Then, use the `genai-logic create` command above.
|
|
69
|
-
3. If logic
|
|
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
|
-
##
|
|
98
|
+
## Working Together
|
|
72
99
|
|
|
73
|
-
When
|
|
100
|
+
When you ask "what should I do now?" or similar:
|
|
74
101
|
|
|
75
|
-
1. **
|
|
76
|
-
2. **
|
|
77
|
-
3. **
|
|
78
|
-
4. **
|
|
79
|
-
5. **
|
|
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
|
api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/.github/.copilot-instructions.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
320
|
-
|
|
397
|
+
from pathlib import Path
|
|
398
|
+
import os
|
|
399
|
+
from dotenv import load_dotenv
|
|
321
400
|
|
|
322
|
-
#
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
|
526
|
+
# Check prices AND flags:
|
|
527
|
+
# sqlite3 db.sqlite "SELECT name, unit_price, carbon_neutral FROM product;"
|
|
406
528
|
|
|
407
|
-
#
|
|
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
|
|
410
|
-
#
|
|
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
|
|
413
|
-
#
|
|
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
|
-
#
|
|
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 #
|
|
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(" ")
|
|
194
194
|
each_line = "## " + each_line
|
|
195
195
|
if each_line.startswith(" Scenario"):
|
|
196
|
-
#
|
|
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(" ")
|
|
217
199
|
wiki_data.append(" ")
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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(" ")
|
|
205
|
+
wiki_data.append(" ")
|
|
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
|