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
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import os
|
|
4
|
+
import ast
|
|
5
|
+
import sys
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
Creates wiki file from test/behave/behave.log, with rule use.
|
|
10
|
+
|
|
11
|
+
Tips
|
|
12
|
+
* use 2 spaces (at end) for newline
|
|
13
|
+
* for tab: & emsp;
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
tab = " "
|
|
18
|
+
behave_debug_info = " # "
|
|
19
|
+
wiki_data = []
|
|
20
|
+
debug_scenario = "XXGood Order Custom Service"
|
|
21
|
+
|
|
22
|
+
scenario_doc_strings = {}
|
|
23
|
+
""" dict of scenario_name, array of strings """
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def remove_trailer(line: str) -> str:
|
|
27
|
+
""" remove everything after the ## """
|
|
28
|
+
end_here = line.find("\t\t##")
|
|
29
|
+
result = line[0:end_here]
|
|
30
|
+
return result
|
|
31
|
+
|
|
32
|
+
def line_spacer():
|
|
33
|
+
wiki_data.append("\n")
|
|
34
|
+
wiki_data.append(" ")
|
|
35
|
+
wiki_data.append(" ")
|
|
36
|
+
wiki_data.append("\n")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_current_readme(prepend_wiki: str):
|
|
40
|
+
""" initialize wiki_data with readme up to {report_name} """
|
|
41
|
+
report_name = "Behave Logic Report"
|
|
42
|
+
with open(prepend_wiki) as readme:
|
|
43
|
+
readme_lines = readme.readlines()
|
|
44
|
+
need_spacer = True
|
|
45
|
+
for each_readme_line in readme_lines:
|
|
46
|
+
if '# ' + report_name in each_readme_line:
|
|
47
|
+
need_spacer = False
|
|
48
|
+
break
|
|
49
|
+
wiki_data.append(each_readme_line[0:-1])
|
|
50
|
+
if need_spacer:
|
|
51
|
+
line_spacer()
|
|
52
|
+
wiki_data.append(f'# {report_name}')
|
|
53
|
+
|
|
54
|
+
def get_truncated_scenario_name(scenario_name: str) -> str:
|
|
55
|
+
""" address max file length (chop at 26), illegal characters """
|
|
56
|
+
scenario_trunc = scenario_name
|
|
57
|
+
if scenario_trunc is not None and len(scenario_trunc) >= 26:
|
|
58
|
+
scenario_trunc = scenario_name[0:25]
|
|
59
|
+
scenario_trunc = f'{str(scenario_trunc).replace(" ", "_")}'
|
|
60
|
+
return scenario_trunc
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def show_logic(scenario: str, logic_logs_dir: str):
|
|
64
|
+
""" insert s{logic_logs_dir}/scenario.log into wiki_data as disclosure area """
|
|
65
|
+
scenario_trunc = get_truncated_scenario_name(scenario)
|
|
66
|
+
logic_file_name = f'{logic_logs_dir}/{scenario_trunc}.log'
|
|
67
|
+
logic_file_name_path = Path(logic_file_name)
|
|
68
|
+
if not logic_file_name_path.is_file(): # debug code
|
|
69
|
+
# wiki_data.append(f'unable to find Logic Log file: {logic_file_name}')
|
|
70
|
+
if scenario == debug_scenario:
|
|
71
|
+
print(f'RELATIVE: {logic_file_name} in {os.getcwd()}')
|
|
72
|
+
full_name = f'{os.getcwd()}/{logic_file_name}'
|
|
73
|
+
print(f'..FULL: {os.getcwd()}/{logic_file_name}')
|
|
74
|
+
logic_file_name = '{logic_logs_dir}/test.log'
|
|
75
|
+
with open(logic_file_name) as logic:
|
|
76
|
+
logic_lines = logic.readlines()
|
|
77
|
+
else:
|
|
78
|
+
logic_log = []
|
|
79
|
+
rules_used = []
|
|
80
|
+
wiki_data.append("<details markdown>")
|
|
81
|
+
wiki_data.append("<summary>Tests - and their logic - are transparent.. click to see Logic</summary>")
|
|
82
|
+
line_spacer()
|
|
83
|
+
scenario_trunc = get_truncated_scenario_name(scenario)
|
|
84
|
+
if scenario_trunc in scenario_doc_strings:
|
|
85
|
+
wiki_data.append(f'**Logic Doc** for scenario: {scenario}')
|
|
86
|
+
wiki_data.append(" ")
|
|
87
|
+
for each_doc_string_line in scenario_doc_strings[scenario_trunc]:
|
|
88
|
+
wiki_data.append(each_doc_string_line[0: -1])
|
|
89
|
+
line_spacer()
|
|
90
|
+
wiki_data.append(f'**Rules Used** in Scenario: {scenario}')
|
|
91
|
+
wiki_data.append("```")
|
|
92
|
+
with open(logic_file_name) as logic:
|
|
93
|
+
logic_lines = logic.readlines()
|
|
94
|
+
is_logic_log = True
|
|
95
|
+
last_rules_start = -1
|
|
96
|
+
last_rules_end = -1
|
|
97
|
+
|
|
98
|
+
# First, find the LAST "These Rules Fired" section
|
|
99
|
+
for i, each_logic_line in enumerate(logic_lines):
|
|
100
|
+
if "These Rules Fired" in each_logic_line:
|
|
101
|
+
last_rules_start = i + 1 # Start collecting from next line
|
|
102
|
+
last_rules_end = -1 # Reset end marker to find the next COMPLETE
|
|
103
|
+
elif last_rules_start > 0 and last_rules_end == -1:
|
|
104
|
+
if 'Logic Phase:' in each_logic_line and 'COMPLETE' in each_logic_line:
|
|
105
|
+
last_rules_end = i
|
|
106
|
+
|
|
107
|
+
# Now process the file, collecting logic log and extracting the last rules section
|
|
108
|
+
for i, each_logic_line in enumerate(logic_lines):
|
|
109
|
+
each_logic_line = remove_trailer(each_logic_line)
|
|
110
|
+
|
|
111
|
+
if is_logic_log:
|
|
112
|
+
if "These Rules Fired" in each_logic_line:
|
|
113
|
+
is_logic_log = False
|
|
114
|
+
else:
|
|
115
|
+
logic_log.append(each_logic_line)
|
|
116
|
+
|
|
117
|
+
# Extract rules from the last "These Rules Fired" section
|
|
118
|
+
if last_rules_start <= i < last_rules_end:
|
|
119
|
+
# Skip empty lines
|
|
120
|
+
if each_logic_line.strip():
|
|
121
|
+
wiki_data.append(each_logic_line + " ")
|
|
122
|
+
|
|
123
|
+
wiki_data.append("```")
|
|
124
|
+
wiki_data.append(f'**Logic Log** in Scenario: {scenario}')
|
|
125
|
+
wiki_data.append("```")
|
|
126
|
+
for each_logic_log in logic_log:
|
|
127
|
+
each_line = remove_trailer(each_logic_log)
|
|
128
|
+
wiki_data.append(each_line)
|
|
129
|
+
wiki_data.append("```")
|
|
130
|
+
wiki_data.append("</details>")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def get_docStrings(steps_dir: str):
|
|
134
|
+
steps_dir_files = os.listdir(steps_dir)
|
|
135
|
+
indent = 4 # skip leading blanks
|
|
136
|
+
for each_steps_dir_file in steps_dir_files:
|
|
137
|
+
each_steps_dir_file_path = Path(steps_dir).joinpath(each_steps_dir_file)
|
|
138
|
+
if each_steps_dir_file_path.is_file():
|
|
139
|
+
with open(each_steps_dir_file_path) as f:
|
|
140
|
+
step_code = f.readlines()
|
|
141
|
+
# print(f'Found File: {str(each_steps_dir_file_path)}')
|
|
142
|
+
for index, each_step_code_line in enumerate(step_code):
|
|
143
|
+
if each_step_code_line.startswith('@when'):
|
|
144
|
+
comment_start = index + 2
|
|
145
|
+
if '"""' in step_code[comment_start]:
|
|
146
|
+
# print(".. found doc string")
|
|
147
|
+
doc_string_line = comment_start+1
|
|
148
|
+
doc_string = []
|
|
149
|
+
while (True):
|
|
150
|
+
if '"""' in step_code[doc_string_line]:
|
|
151
|
+
break
|
|
152
|
+
doc_string.append(step_code[doc_string_line][indent:])
|
|
153
|
+
doc_string_line += 1
|
|
154
|
+
scenario_line = doc_string_line+1
|
|
155
|
+
if 'scenario_name' not in step_code[scenario_line]:
|
|
156
|
+
print(f'\n** Warning - scenario_name not found '\
|
|
157
|
+
f'in file {str(each_steps_dir_file_path)}, '\
|
|
158
|
+
f'after line {scenario_line} -- skipped')
|
|
159
|
+
else:
|
|
160
|
+
scenario_code_line = step_code[scenario_line]
|
|
161
|
+
scenario_name_start = scenario_code_line.find("'") + 1
|
|
162
|
+
scenario_name_end = scenario_code_line[scenario_name_start+1:].find("'")
|
|
163
|
+
scenario_name = scenario_code_line[scenario_name_start:
|
|
164
|
+
scenario_name_end + scenario_name_start+1]
|
|
165
|
+
if scenario_name == debug_scenario:
|
|
166
|
+
print(f'got {debug_scenario}')
|
|
167
|
+
scenario_trunc = get_truncated_scenario_name(scenario_name)
|
|
168
|
+
# print(f'.... truncated scenario_name: {scenario_trunc} in {scenario_code_line}')
|
|
169
|
+
scenario_doc_strings[scenario_trunc] = doc_string
|
|
170
|
+
# print("that's all, folks")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def main(behave_log: str, scenario_logs: str, wiki: str, prepend_wiki: str):
|
|
174
|
+
""" main driver """
|
|
175
|
+
get_docStrings(steps_dir="features/steps")
|
|
176
|
+
|
|
177
|
+
get_current_readme(prepend_wiki=prepend_wiki)
|
|
178
|
+
|
|
179
|
+
contents = None
|
|
180
|
+
with open(behave_log) as f:
|
|
181
|
+
contents = f.readlines()
|
|
182
|
+
|
|
183
|
+
just_saw_then = False
|
|
184
|
+
current_scenario = ""
|
|
185
|
+
previous_scenario = ""
|
|
186
|
+
for each_line in contents:
|
|
187
|
+
if just_saw_then and each_line == "\n":
|
|
188
|
+
show_logic(scenario=current_scenario, logic_logs_dir=scenario_logs)
|
|
189
|
+
just_saw_then = False
|
|
190
|
+
previous_scenario = ""
|
|
191
|
+
if each_line.startswith("Feature"):
|
|
192
|
+
wiki_data.append(" ")
|
|
193
|
+
wiki_data.append(" ")
|
|
194
|
+
each_line = "## " + each_line
|
|
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:
|
|
214
|
+
current_scenario = each_line[18:]
|
|
215
|
+
previous_scenario = current_scenario
|
|
216
|
+
wiki_data.append(" ")
|
|
217
|
+
wiki_data.append(" ")
|
|
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()
|
|
@@ -1,72 +1,81 @@
|
|
|
1
1
|
Feature: Order Processing with Business Logic
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# Tests all declarative rules:
|
|
4
|
+
# 1. Customer.balance = sum(Order.amount_total) WHERE date_shipped is None
|
|
5
|
+
# 2. Order.amount_total = sum(Item.amount)
|
|
6
|
+
# 3. Item.amount = quantity * unit_price (with 10% carbon neutral discount for qty >= 10)
|
|
7
|
+
# 4. Item.unit_price copied from Product.unit_price
|
|
8
|
+
# 5. Customer.balance <= credit_limit constraint
|
|
9
|
+
# 6. Kafka integration when order shipped
|
|
5
10
|
|
|
6
11
|
Scenario: Good Order Placed via B2B API
|
|
7
|
-
Given Customer "Alice" with balance 0 and credit limit
|
|
12
|
+
Given Customer "Alice" with balance 0 and credit limit 5000
|
|
8
13
|
When B2B order placed for "Alice" with 5 Widget
|
|
9
14
|
Then Customer balance should be 450
|
|
10
15
|
And Order amount_total should be 450
|
|
11
|
-
And
|
|
16
|
+
And Item amount should be 450
|
|
17
|
+
And Item unit_price should be 90
|
|
12
18
|
|
|
13
|
-
Scenario: Item
|
|
14
|
-
Given Customer "Bob" with balance 0 and credit limit
|
|
15
|
-
|
|
19
|
+
Scenario: Multi-Item Order via B2B API
|
|
20
|
+
Given Customer "Bob" with balance 0 and credit limit 3000
|
|
21
|
+
When B2B order placed for "Bob" with 3 Widget and 2 Gadget
|
|
22
|
+
Then Customer balance should be 570
|
|
23
|
+
And Order amount_total should be 570
|
|
24
|
+
|
|
25
|
+
Scenario: Carbon Neutral Discount Applied
|
|
26
|
+
Given Customer "Diana" with balance 0 and credit limit 5000
|
|
27
|
+
When B2B order placed for "Diana" with 10 carbon neutral Gadget
|
|
28
|
+
Then Customer balance should be 1350
|
|
29
|
+
And Item amount should be 1350
|
|
30
|
+
|
|
31
|
+
Scenario: Item Quantity Change
|
|
32
|
+
Given Customer "Charlie" with balance 0 and credit limit 2000
|
|
33
|
+
And Order is created for "Charlie" with 5 Widget
|
|
16
34
|
When Item quantity changed to 10
|
|
17
35
|
Then Item amount should be 900
|
|
18
36
|
And Order amount_total should be 900
|
|
19
37
|
And Customer balance should be 900
|
|
20
38
|
|
|
21
|
-
Scenario: Change
|
|
22
|
-
Given Customer "
|
|
23
|
-
And
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
And
|
|
39
|
+
Scenario: Change Product in Item
|
|
40
|
+
Given Customer "Alice" with balance 0 and credit limit 5000
|
|
41
|
+
And Order is created for "Alice" with 5 Widget
|
|
42
|
+
When Item product changed to "Gadget"
|
|
43
|
+
Then Item unit_price should be 150
|
|
44
|
+
And Item amount should be 750
|
|
45
|
+
And Order amount_total should be 750
|
|
46
|
+
And Customer balance should be 750
|
|
28
47
|
|
|
29
|
-
Scenario: Delete Item Reduces Order
|
|
30
|
-
Given Customer "
|
|
31
|
-
And Order
|
|
32
|
-
When First item deleted
|
|
48
|
+
Scenario: Delete Item Reduces Order
|
|
49
|
+
Given Customer "Bob" with balance 0 and credit limit 3000
|
|
50
|
+
And Order is created for "Bob" with 3 Widget and 2 Gadget
|
|
51
|
+
When First item is deleted
|
|
33
52
|
Then Order amount_total should be 300
|
|
34
53
|
And Customer balance should be 300
|
|
35
54
|
|
|
36
|
-
Scenario:
|
|
37
|
-
Given Customer "
|
|
38
|
-
And
|
|
55
|
+
Scenario: Change Order Customer
|
|
56
|
+
Given Customer "Alice" with balance 0 and credit limit 5000
|
|
57
|
+
And Customer "Bob" with balance 0 and credit limit 3000
|
|
58
|
+
And Order is created for "Alice" with 5 Widget
|
|
59
|
+
When Order customer changed to "Bob"
|
|
60
|
+
Then Customer "Alice" balance should be 0
|
|
61
|
+
And Customer "Bob" balance should be 450
|
|
62
|
+
|
|
63
|
+
Scenario: Ship Order Excludes from Balance
|
|
64
|
+
Given Customer "Charlie" with balance 0 and credit limit 2000
|
|
65
|
+
And Order is created for "Charlie" with 2 Widget
|
|
39
66
|
When Order is shipped
|
|
40
67
|
Then Customer balance should be 0
|
|
68
|
+
And Order amount_total should be 180
|
|
41
69
|
|
|
42
|
-
Scenario: Unship Order Includes in Balance
|
|
43
|
-
Given Customer "
|
|
44
|
-
And Shipped order
|
|
70
|
+
Scenario: Unship Order Includes in Balance
|
|
71
|
+
Given Customer "Diana" with balance 0 and credit limit 5000
|
|
72
|
+
And Shipped order is created for "Diana" with 3 Gadget
|
|
45
73
|
When Order is unshipped
|
|
46
|
-
Then Customer balance should be
|
|
47
|
-
|
|
48
|
-
Scenario: Exceed Credit Limit Rejected (Constraint FAIL)
|
|
49
|
-
Given Customer "LimitTest" with balance 0 and credit limit 500
|
|
50
|
-
When B2B order placed for "LimitTest" with 10 Gadget
|
|
51
|
-
Then Order creation should fail
|
|
52
|
-
And Error message should contain "credit limit"
|
|
53
|
-
|
|
54
|
-
Scenario: Carbon Neutral Discount Applied (Custom Logic)
|
|
55
|
-
Given Customer "GreenBuyer" with balance 0 and credit limit 2000
|
|
56
|
-
When B2B order placed for "GreenBuyer" with 10 carbon neutral Gadget
|
|
57
|
-
Then Item amount should be 1350
|
|
58
|
-
And Order amount_total should be 1350
|
|
59
|
-
And Customer balance should be 1350
|
|
60
|
-
|
|
61
|
-
Scenario: Product Unit Price Copied to Item
|
|
62
|
-
Given Customer "PriceCopy" with balance 0 and credit limit 3000
|
|
63
|
-
When B2B order placed for "PriceCopy" with 1 Green
|
|
64
|
-
Then Item unit_price should be 109
|
|
74
|
+
Then Customer balance should be 450
|
|
75
|
+
And Order amount_total should be 450
|
|
65
76
|
|
|
66
|
-
Scenario:
|
|
67
|
-
Given Customer "
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
And Item amount should be 300
|
|
72
|
-
And Order amount_total should be 300
|
|
77
|
+
Scenario: Exceed Credit Limit Rejected
|
|
78
|
+
Given Customer "Silent" with balance 0 and credit limit 1000
|
|
79
|
+
When B2B order placed for "Silent" with 20 Widget
|
|
80
|
+
Then Order should be rejected
|
|
81
|
+
And Error message should contain "exceeds credit limit"
|