stoobly-agent 1.9.0__py3-none-any.whl → 1.9.1__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.
- stoobly_agent/__init__.py +1 -1
- stoobly_agent/app/models/factories/resource/local_db/helpers/log.py +52 -2
- stoobly_agent/app/models/factories/resource/local_db/helpers/request_snapshot.py +0 -1
- stoobly_agent/app/models/factories/resource/local_db/helpers/scenario_snapshot.py +2 -3
- stoobly_agent/app/models/factories/resource/local_db/helpers/snapshot_service.py +9 -8
- stoobly_agent/app/models/helpers/apply.py +9 -4
- stoobly_agent/test/app/cli/snapshot/snapshot_apply_test.py +143 -1
- stoobly_agent/test/app/cli/snapshot/snapshot_prune_test.py +72 -3
- stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
- {stoobly_agent-1.9.0.dist-info → stoobly_agent-1.9.1.dist-info}/METADATA +1 -1
- {stoobly_agent-1.9.0.dist-info → stoobly_agent-1.9.1.dist-info}/RECORD +14 -14
- {stoobly_agent-1.9.0.dist-info → stoobly_agent-1.9.1.dist-info}/LICENSE +0 -0
- {stoobly_agent-1.9.0.dist-info → stoobly_agent-1.9.1.dist-info}/WHEEL +0 -0
- {stoobly_agent-1.9.0.dist-info → stoobly_agent-1.9.1.dist-info}/entry_points.txt +0 -0
stoobly_agent/__init__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
COMMAND = 'stoobly-agent'
|
2
|
-
VERSION = '1.9.
|
2
|
+
VERSION = '1.9.1'
|
@@ -8,6 +8,7 @@ from stoobly_agent.config.data_dir import DataDir
|
|
8
8
|
from stoobly_agent.lib.logger import bcolors, Logger
|
9
9
|
|
10
10
|
from .log_event import LogEvent
|
11
|
+
from .request_snapshot import RequestSnapshot
|
11
12
|
from .snapshot_types import DELETE_ACTION, PUT_ACTION, Resource
|
12
13
|
|
13
14
|
EVENT_DELIMITTER = "\n"
|
@@ -67,6 +68,25 @@ class Log():
|
|
67
68
|
contents = self.read()
|
68
69
|
return self.build_raw_events(contents)
|
69
70
|
|
71
|
+
@property
|
72
|
+
def scenario_inverted_index(self):
|
73
|
+
index = {}
|
74
|
+
|
75
|
+
def handle_snapshot(snapshot: RequestSnapshot):
|
76
|
+
request_uuid = snapshot.uuid
|
77
|
+
if not request_uuid in index:
|
78
|
+
index[request_uuid] = []
|
79
|
+
|
80
|
+
index[request_uuid].append(event.resource_uuid)
|
81
|
+
|
82
|
+
for event in self.target_events:
|
83
|
+
if not event.is_scenario():
|
84
|
+
continue
|
85
|
+
|
86
|
+
event.snapshot().iter_request_snapshots(handle_snapshot)
|
87
|
+
|
88
|
+
return index
|
89
|
+
|
70
90
|
@property
|
71
91
|
def unprocessed_events(self) -> List[LogEvent]:
|
72
92
|
events = self.events
|
@@ -197,12 +217,18 @@ class Log():
|
|
197
217
|
|
198
218
|
resource_index[event.resource_uuid].append(event)
|
199
219
|
|
220
|
+
scenario_inverted_index = self.scenario_inverted_index
|
221
|
+
|
200
222
|
pruned_events = self.collapse(events)
|
201
223
|
for event in pruned_events:
|
202
224
|
snapshot = event.snapshot()
|
203
225
|
snapshot_exists = snapshot.exists
|
204
226
|
|
205
227
|
if event.action == DELETE_ACTION or not snapshot_exists:
|
228
|
+
if event.is_request() and event.resource_uuid in scenario_inverted_index:
|
229
|
+
# If a request is deleted, only prune if it's not also a part of a scenario
|
230
|
+
continue
|
231
|
+
|
206
232
|
Logger.instance(LOG_ID).info(f"{bcolors.OKBLUE}Removing{bcolors.ENDC} {event.resource} {event.resource_uuid}")
|
207
233
|
|
208
234
|
resource_events: List[LogEvent] = resource_index[event.resource_uuid]
|
@@ -218,8 +244,16 @@ class Log():
|
|
218
244
|
removed_events[event.uuid] = True
|
219
245
|
|
220
246
|
if event.action == DELETE_ACTION and snapshot_exists:
|
221
|
-
if
|
247
|
+
if dry_run:
|
248
|
+
continue
|
249
|
+
|
250
|
+
if event.is_scenario():
|
251
|
+
# We still need to check each request in a scenario to make sure another scenario does not depend on it
|
252
|
+
snapshot.remove(lambda snapshot: self.remove_request_snapshot(snapshot, scenario_inverted_index))
|
253
|
+
elif event.is_request():
|
254
|
+
# We have already checked that a scenario does not depend on the request above
|
222
255
|
snapshot.remove()
|
256
|
+
|
223
257
|
Logger.instance(LOG_ID).info(f"Removing {event.resource} snapshot")
|
224
258
|
|
225
259
|
def build_raw_events(self, contents: str) -> List[str]:
|
@@ -242,6 +276,8 @@ class Log():
|
|
242
276
|
'''
|
243
277
|
Remove DELETE events where the last processed event was a PUT
|
244
278
|
'''
|
279
|
+
|
280
|
+
# Build an index such that if the last event is DELETE_ACTION, then it will NOT exist in the index
|
245
281
|
index = {}
|
246
282
|
for event in processed_events:
|
247
283
|
if event.action == PUT_ACTION:
|
@@ -249,10 +285,18 @@ class Log():
|
|
249
285
|
elif event.action == DELETE_ACTION:
|
250
286
|
if event.resource_uuid in index:
|
251
287
|
del index[event.resource_uuid]
|
288
|
+
|
289
|
+
scenario_inverted_index = self.scenario_inverted_index
|
290
|
+
|
291
|
+
def keep(e: LogEvent):
|
292
|
+
if e.action != DELETE_ACTION:
|
293
|
+
return True
|
294
|
+
|
295
|
+
return e.action == DELETE_ACTION and e.is_request() and e.resource_uuid in scenario_inverted_index
|
252
296
|
|
253
297
|
return list(
|
254
298
|
filter(
|
255
|
-
|
299
|
+
keep,
|
256
300
|
unprocessed_events
|
257
301
|
)
|
258
302
|
)
|
@@ -289,6 +333,12 @@ class Log():
|
|
289
333
|
remaining_events = self.remove_dangling_events(processed_events, unprocessed_events)
|
290
334
|
return list(filter(lambda e: e.uuid not in remaining_version_uuids, remaining_events))
|
291
335
|
|
336
|
+
def remove_request_snapshot(self, snapshot: RequestSnapshot, scenario_inverted_index: dict = None):
|
337
|
+
scenario_inverted_index = scenario_inverted_index or self.scenario_inverted_index
|
338
|
+
|
339
|
+
if snapshot.uuid not in scenario_inverted_index:
|
340
|
+
snapshot.remove()
|
341
|
+
|
292
342
|
# Rotate log to history
|
293
343
|
def rotate(self):
|
294
344
|
if not os.path.exists(self.__log_file_path):
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import json
|
2
2
|
import os
|
3
3
|
import pdb
|
4
|
-
import shutil
|
5
4
|
|
6
5
|
from typing import Callable, List
|
7
6
|
|
@@ -128,12 +127,12 @@ class ScenarioSnapshot(Snapshot):
|
|
128
127
|
if os.path.exists(metadata_file_path):
|
129
128
|
os.remove(metadata_file_path)
|
130
129
|
|
131
|
-
def remove_requests(self):
|
130
|
+
def remove_requests(self, handle_remove_request_snapshot = None):
|
132
131
|
requests_file_path = self.requests_path
|
133
132
|
|
134
133
|
if os.path.exists(requests_file_path):
|
135
134
|
# A request only every belongs to one scenario
|
136
|
-
self.iter_request_snapshots(self.__handle_remove_requests)
|
135
|
+
self.iter_request_snapshots(handle_remove_request_snapshot or self.__handle_remove_requests)
|
137
136
|
|
138
137
|
os.remove(requests_file_path)
|
139
138
|
|
@@ -14,15 +14,17 @@ def snapshot_request(request: Request, **options: RequestSnapshotOptions):
|
|
14
14
|
return
|
15
15
|
|
16
16
|
snapshot = RequestSnapshot(request.uuid)
|
17
|
-
|
18
17
|
snapshot.backup()
|
19
18
|
|
19
|
+
log = Log()
|
20
|
+
|
20
21
|
if action == PUT_ACTION:
|
21
22
|
snapshot.write(request, **options)
|
22
23
|
elif action == DELETE_ACTION:
|
23
|
-
|
24
|
+
inverted_scenario_index = log.scenario_inverted_index
|
24
25
|
|
25
|
-
|
26
|
+
# If a scenario currently depends on this request, we can't remove the snapshot until the scenario is also removed
|
27
|
+
log.remove_request_snapshot(snapshot, inverted_scenario_index)
|
26
28
|
|
27
29
|
try:
|
28
30
|
if action == PUT_ACTION:
|
@@ -40,7 +42,6 @@ def snapshot_scenario(scenario: Scenario, **options):
|
|
40
42
|
return
|
41
43
|
|
42
44
|
snapshot = ScenarioSnapshot(scenario.uuid)
|
43
|
-
|
44
45
|
snapshot.backup_metadata()
|
45
46
|
|
46
47
|
if action == PUT_ACTION:
|
@@ -50,13 +51,13 @@ def snapshot_scenario(scenario: Scenario, **options):
|
|
50
51
|
|
51
52
|
snapshot.backup_requests()
|
52
53
|
|
54
|
+
log = Log()
|
55
|
+
inverted_scenario_index = log.scenario_inverted_index
|
53
56
|
if action == PUT_ACTION:
|
54
|
-
snapshot.remove_requests()
|
57
|
+
snapshot.remove_requests(lambda snapshot: log.remove_request_snapshot(snapshot, inverted_scenario_index))
|
55
58
|
snapshot.write_requests(scenario, **options)
|
56
59
|
elif action == DELETE_ACTION:
|
57
|
-
snapshot.remove_requests()
|
58
|
-
|
59
|
-
log = Log()
|
60
|
+
snapshot.remove_requests(lambda snapshot: log.remove_request_snapshot(snapshot, inverted_scenario_index))
|
60
61
|
|
61
62
|
if action == PUT_ACTION:
|
62
63
|
log.put(scenario)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
import pdb
|
2
2
|
|
3
|
+
from typing import List
|
4
|
+
|
3
5
|
from stoobly_agent.app.models.factories.resource.local_db.helpers.log import Log
|
4
6
|
from stoobly_agent.app.models.factories.resource.local_db.helpers.request_snapshot import RequestSnapshot
|
5
7
|
from stoobly_agent.app.models.factories.resource.local_db.helpers.scenario_snapshot import ScenarioSnapshot
|
@@ -71,6 +73,7 @@ class Apply():
|
|
71
73
|
if results:
|
72
74
|
status = results[1]
|
73
75
|
if status == 0 or status >= 400:
|
76
|
+
self.__logger(f"{bcolors.FAIL}Error{bcolors.ENDC} {results[0]}")
|
74
77
|
completed = False
|
75
78
|
break
|
76
79
|
|
@@ -218,14 +221,16 @@ class Apply():
|
|
218
221
|
|
219
222
|
snapshot_requests = {}
|
220
223
|
|
221
|
-
request_snapshots = snapshot.request_snapshots
|
224
|
+
request_snapshots: List[RequestSnapshot] = snapshot.request_snapshots
|
222
225
|
for request_snapshot in request_snapshots:
|
223
226
|
raw_request = request_snapshot.request
|
224
|
-
|
225
|
-
toks = raw_request.split(REQUEST_STRING_CLRF, 1)
|
226
227
|
|
228
|
+
if not raw_request:
|
229
|
+
return f"{request_snapshot.path} is missing", 400
|
230
|
+
|
231
|
+
toks = raw_request.split(REQUEST_STRING_CLRF, 1)
|
227
232
|
if len(toks) != 2:
|
228
|
-
return f"{
|
233
|
+
return f"{request_snapshot.path} contains an invalid request", 400
|
229
234
|
|
230
235
|
uuid = request_snapshot.uuid
|
231
236
|
res, status = self.__put_request(uuid, raw_request, scenario_id=scenario['id'])
|
@@ -547,4 +547,146 @@ class TestApply():
|
|
547
547
|
recreated_scenario = Scenario.find_by(uuid=created_scenario.uuid)
|
548
548
|
assert recreated_scenario
|
549
549
|
|
550
|
-
assert len(recreated_scenario.requests) == 0
|
550
|
+
assert len(recreated_scenario.requests) == 0
|
551
|
+
|
552
|
+
class TestWhenDeletingRequestWhileReferenced():
|
553
|
+
|
554
|
+
class TestWhenRemoveScenarioRequest():
|
555
|
+
'''
|
556
|
+
1. Create scenario
|
557
|
+
2. Add 2 requests to it
|
558
|
+
3. Snapshot scenario
|
559
|
+
4. Snapshot requests with action DELETE_ACTION
|
560
|
+
5. Apply
|
561
|
+
6. Expect scenario to have 1 request
|
562
|
+
'''
|
563
|
+
|
564
|
+
@pytest.fixture(scope='class')
|
565
|
+
def created_scenario(self, runner: CliRunner):
|
566
|
+
create_result = runner.invoke(scenario, ['create', 'test'])
|
567
|
+
assert create_result.exit_code == 0
|
568
|
+
return Scenario.last()
|
569
|
+
|
570
|
+
@pytest.fixture(scope='class', autouse=True)
|
571
|
+
def created_scenario_requests(self, runner: CliRunner, created_scenario: Scenario):
|
572
|
+
record_result = runner.invoke(record, ['--scenario-key', created_scenario.key(), DETERMINISTIC_GET_REQUEST_URL])
|
573
|
+
assert record_result.exit_code == 0
|
574
|
+
|
575
|
+
record_result = runner.invoke(record, ['--scenario-key', created_scenario.key(), NON_DETERMINISTIC_GET_REQUEST_URL])
|
576
|
+
assert record_result.exit_code == 0
|
577
|
+
|
578
|
+
return created_scenario.requests
|
579
|
+
|
580
|
+
@pytest.fixture(scope='class', autouse=True)
|
581
|
+
def delete_event(self, runner: CliRunner, created_scenario: Scenario, created_scenario_requests: List[Request]):
|
582
|
+
snapshot_result = runner.invoke(scenario, ['snapshot', created_scenario.key()])
|
583
|
+
assert snapshot_result.exit_code == 0
|
584
|
+
|
585
|
+
time.sleep(0.5) # So events do not have the same uuid
|
586
|
+
|
587
|
+
created_request = created_scenario_requests[1]
|
588
|
+
snapshot_result = runner.invoke(request, ['snapshot', created_request.key(), '--action', DELETE_ACTION])
|
589
|
+
assert snapshot_result.exit_code == 0
|
590
|
+
|
591
|
+
log = Log()
|
592
|
+
events = log.events
|
593
|
+
return events[len(events) - 1]
|
594
|
+
|
595
|
+
@pytest.fixture(scope='class')
|
596
|
+
def apply_result(self, runner: CliRunner, created_scenario: Scenario):
|
597
|
+
created_scenario = Scenario.find(created_scenario.id)
|
598
|
+
assert created_scenario.requests_count == 2
|
599
|
+
apply_result = runner.invoke(snapshot, ['apply'])
|
600
|
+
|
601
|
+
return apply_result
|
602
|
+
|
603
|
+
def test_it_updates_scenario(self, created_scenario: Scenario, apply_result):
|
604
|
+
assert apply_result.exit_code == 0
|
605
|
+
|
606
|
+
created_scenario = Scenario.find(created_scenario.id)
|
607
|
+
assert created_scenario.requests_count == 1
|
608
|
+
|
609
|
+
def test_it_maintains_requests(self, created_scenario: Scenario, created_scenario_requests: List[Request]):
|
610
|
+
created_scenario = Scenario.find(created_scenario.id)
|
611
|
+
assert len(created_scenario.requests) == 1
|
612
|
+
|
613
|
+
requests = created_scenario.requests
|
614
|
+
|
615
|
+
assert_orm_request_equivalent(requests[0], created_scenario_requests[0])
|
616
|
+
|
617
|
+
class TestWhenMovingScenarioRequest():
|
618
|
+
'''
|
619
|
+
1. Create scenario one and scenario two
|
620
|
+
2. Add 1 request to scenario one
|
621
|
+
3. Snapshot scenario one
|
622
|
+
4. Move scenario one request to scenario two
|
623
|
+
5. Snapshot scenario two
|
624
|
+
5. Apply
|
625
|
+
6. Expect scenario one to have 0 requests
|
626
|
+
7. Expect scenario two to have 1 request
|
627
|
+
'''
|
628
|
+
|
629
|
+
@pytest.fixture(scope='class')
|
630
|
+
def created_scenario_one(self, runner: CliRunner):
|
631
|
+
create_result = runner.invoke(scenario, ['create', 'test1'])
|
632
|
+
assert create_result.exit_code == 0
|
633
|
+
return Scenario.last()
|
634
|
+
|
635
|
+
@pytest.fixture(scope='class')
|
636
|
+
def created_scenario_two(self, runner: CliRunner):
|
637
|
+
create_result = runner.invoke(scenario, ['create', 'test2'])
|
638
|
+
assert create_result.exit_code == 0
|
639
|
+
return Scenario.last()
|
640
|
+
|
641
|
+
@pytest.fixture(scope='class', autouse=True)
|
642
|
+
def created_scenario_request(self, runner: CliRunner, created_scenario_one: Scenario):
|
643
|
+
record_result = runner.invoke(record, ['--scenario-key', created_scenario_one.key(), DETERMINISTIC_GET_REQUEST_URL])
|
644
|
+
assert record_result.exit_code == 0
|
645
|
+
|
646
|
+
return created_scenario_one.requests[0]
|
647
|
+
|
648
|
+
@pytest.fixture(scope='class', autouse=True)
|
649
|
+
def put_event_one(self, runner: CliRunner, created_scenario_one: Scenario, created_scenario_request: Request):
|
650
|
+
snapshot_result = runner.invoke(scenario, ['snapshot', created_scenario_one.key()])
|
651
|
+
assert snapshot_result.exit_code == 0
|
652
|
+
|
653
|
+
log = Log()
|
654
|
+
events = log.events
|
655
|
+
return events[len(events) - 1]
|
656
|
+
|
657
|
+
@pytest.fixture(scope='class', autouse=True)
|
658
|
+
def put_event_two(self, runner: CliRunner, created_scenario_two: Scenario, created_scenario_request: Request):
|
659
|
+
created_scenario_request.update(scenario_id=created_scenario_two.id)
|
660
|
+
|
661
|
+
snapshot_result = runner.invoke(scenario, ['snapshot', created_scenario_two.key()])
|
662
|
+
assert snapshot_result.exit_code == 0
|
663
|
+
|
664
|
+
log = Log()
|
665
|
+
events = log.events
|
666
|
+
return events[len(events) - 1]
|
667
|
+
|
668
|
+
@pytest.fixture(scope='class')
|
669
|
+
def apply_result(self, runner: CliRunner):
|
670
|
+
apply_result = runner.invoke(snapshot, ['apply'])
|
671
|
+
|
672
|
+
return apply_result
|
673
|
+
|
674
|
+
def test_it_removes_request_from_scenario_one(self, created_scenario_one: Scenario, apply_result):
|
675
|
+
assert apply_result.exit_code == 0
|
676
|
+
|
677
|
+
created_scenario_one = Scenario.find(created_scenario_one.id)
|
678
|
+
assert created_scenario_one.requests_count == 0
|
679
|
+
|
680
|
+
def test_it_adds_request_to_scenario_two(self, created_scenario_two: Scenario, apply_result):
|
681
|
+
assert apply_result.exit_code == 0
|
682
|
+
|
683
|
+
created_scenario_two = Scenario.find(created_scenario_two.id)
|
684
|
+
assert created_scenario_two.requests_count == 1
|
685
|
+
|
686
|
+
def test_it_maintains_requests(self, created_scenario_two: Scenario, created_scenario_request):
|
687
|
+
created_scenario_two = Scenario.find(created_scenario_two.id)
|
688
|
+
assert len(created_scenario_two.requests) == 1
|
689
|
+
|
690
|
+
requests = created_scenario_two.requests
|
691
|
+
|
692
|
+
assert_orm_request_equivalent(requests[0], created_scenario_request)
|
@@ -3,12 +3,14 @@ import pytest
|
|
3
3
|
import time
|
4
4
|
|
5
5
|
from click.testing import CliRunner
|
6
|
+
from typing import List
|
6
7
|
|
7
8
|
from stoobly_agent.app.models.factories.resource.local_db.helpers.log import Log
|
8
9
|
from stoobly_agent.app.models.factories.resource.local_db.helpers.log_event import DELETE_ACTION, LogEvent
|
9
|
-
from stoobly_agent.cli import record, request, snapshot
|
10
|
+
from stoobly_agent.cli import record, request, scenario, snapshot
|
10
11
|
from stoobly_agent.lib.orm.request import Request
|
11
|
-
from stoobly_agent.
|
12
|
+
from stoobly_agent.lib.orm.scenario import Scenario
|
13
|
+
from stoobly_agent.test.test_helper import assert_orm_request_equivalent, DETERMINISTIC_GET_REQUEST_URL, NON_DETERMINISTIC_GET_REQUEST_URL, reset
|
12
14
|
|
13
15
|
@pytest.fixture(scope='module')
|
14
16
|
def runner():
|
@@ -107,4 +109,71 @@ class TestPrune():
|
|
107
109
|
|
108
110
|
apply_result = runner.invoke(snapshot, ['prune'])
|
109
111
|
assert apply_result.exit_code == 0
|
110
|
-
assert len(log.events) == 0
|
112
|
+
assert len(log.events) == 0
|
113
|
+
|
114
|
+
class TestWhenRemoveScenarioRequest():
|
115
|
+
'''
|
116
|
+
1. Create scenario
|
117
|
+
2. Add 2 requests to it
|
118
|
+
3. Snapshot scenario
|
119
|
+
4. Snapshot request with action DELETE_ACTION
|
120
|
+
5. Prune, but because scenario depends on the request, should not be able to prune
|
121
|
+
6. Apply
|
122
|
+
7. Expect scenario to have 1 request
|
123
|
+
'''
|
124
|
+
|
125
|
+
@pytest.fixture(scope='class')
|
126
|
+
def created_scenario(self, runner: CliRunner):
|
127
|
+
create_result = runner.invoke(scenario, ['create', 'test'])
|
128
|
+
assert create_result.exit_code == 0
|
129
|
+
return Scenario.last()
|
130
|
+
|
131
|
+
@pytest.fixture(scope='class', autouse=True)
|
132
|
+
def created_scenario_requests(self, runner: CliRunner, created_scenario: Scenario):
|
133
|
+
record_result = runner.invoke(record, ['--scenario-key', created_scenario.key(), DETERMINISTIC_GET_REQUEST_URL])
|
134
|
+
assert record_result.exit_code == 0
|
135
|
+
|
136
|
+
record_result = runner.invoke(record, ['--scenario-key', created_scenario.key(), NON_DETERMINISTIC_GET_REQUEST_URL])
|
137
|
+
assert record_result.exit_code == 0
|
138
|
+
|
139
|
+
return created_scenario.requests
|
140
|
+
|
141
|
+
@pytest.fixture(scope='class', autouse=True)
|
142
|
+
def delete_event(self, runner: CliRunner, created_scenario: Scenario, created_scenario_requests: List[Request]):
|
143
|
+
snapshot_result = runner.invoke(scenario, ['snapshot', created_scenario.key()])
|
144
|
+
assert snapshot_result.exit_code == 0
|
145
|
+
|
146
|
+
time.sleep(0.5) # So events do not have the same uuid
|
147
|
+
|
148
|
+
created_request = created_scenario_requests[1]
|
149
|
+
snapshot_result = runner.invoke(request, ['snapshot', created_request.key(), '--action', DELETE_ACTION])
|
150
|
+
assert snapshot_result.exit_code == 0
|
151
|
+
|
152
|
+
log = Log()
|
153
|
+
events = log.events
|
154
|
+
return events[len(events) - 1]
|
155
|
+
|
156
|
+
@pytest.fixture(scope='class')
|
157
|
+
def apply_result(self, runner: CliRunner, created_scenario: Scenario):
|
158
|
+
prune_result = runner.invoke(snapshot, ['prune'])
|
159
|
+
assert prune_result.exit_code == 0
|
160
|
+
|
161
|
+
created_scenario = Scenario.find(created_scenario.id)
|
162
|
+
assert created_scenario.requests_count == 2
|
163
|
+
apply_result = runner.invoke(snapshot, ['apply'])
|
164
|
+
|
165
|
+
return apply_result
|
166
|
+
|
167
|
+
def test_it_updates_scenario(self, created_scenario: Scenario, apply_result):
|
168
|
+
assert apply_result.exit_code == 0
|
169
|
+
|
170
|
+
created_scenario = Scenario.find(created_scenario.id)
|
171
|
+
assert created_scenario.requests_count == 1
|
172
|
+
|
173
|
+
def test_it_maintains_requests(self, created_scenario: Scenario, created_scenario_requests: List[Request]):
|
174
|
+
created_scenario = Scenario.find(created_scenario.id)
|
175
|
+
assert len(created_scenario.requests) == 1
|
176
|
+
|
177
|
+
requests = created_scenario.requests
|
178
|
+
|
179
|
+
assert_orm_request_equivalent(requests[0], created_scenario_requests[0])
|
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.9.0
|
@@ -1,4 +1,4 @@
|
|
1
|
-
stoobly_agent/__init__.py,sha256=
|
1
|
+
stoobly_agent/__init__.py,sha256=ehByLp1z2dzieXsaNQLADxDog0UyQfS9wSZ3n9PqZNs,44
|
2
2
|
stoobly_agent/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
stoobly_agent/app/api/__init__.py,sha256=ctkB8KR-eXO0SFhj602huHiyvQ3PslFWd8fkcufgrAI,1000
|
4
4
|
stoobly_agent/app/api/application_http_request_handler.py,sha256=Vvz53yB0bR7J-QqMAkLlhcZrA4P64ZEN7w8cMbgl6o0,5261
|
@@ -258,14 +258,14 @@ stoobly_agent/app/models/factories/resource/local_db/body_adapter.py,sha256=lrnI
|
|
258
258
|
stoobly_agent/app/models/factories/resource/local_db/header_adapter.py,sha256=NQdCErFtJL7sBaLpKLYfJSEA3AiaaVuU7LUcGJ-dHOI,3104
|
259
259
|
stoobly_agent/app/models/factories/resource/local_db/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
260
260
|
stoobly_agent/app/models/factories/resource/local_db/helpers/create_request_columns_service.py,sha256=HABMelW-cvhm2WXaywbQcd4PQhzSrz4vAbN7uOdetXM,1541
|
261
|
-
stoobly_agent/app/models/factories/resource/local_db/helpers/log.py,sha256=
|
261
|
+
stoobly_agent/app/models/factories/resource/local_db/helpers/log.py,sha256=T0PCrMX2s7PMwZSMdr436PzNa0QQTCBDBshosgZ24BI,10899
|
262
262
|
stoobly_agent/app/models/factories/resource/local_db/helpers/log_event.py,sha256=30wWhqibTWgnpibosclaB9OCWzArL7R4krGS8WW2bGY,3584
|
263
263
|
stoobly_agent/app/models/factories/resource/local_db/helpers/request_builder.py,sha256=PyVvsYmi5bBQ6PsUPxQ4nJM9rhhjGVOTd7ipuc2tMMM,3227
|
264
|
-
stoobly_agent/app/models/factories/resource/local_db/helpers/request_snapshot.py,sha256=
|
265
|
-
stoobly_agent/app/models/factories/resource/local_db/helpers/scenario_snapshot.py,sha256=
|
264
|
+
stoobly_agent/app/models/factories/resource/local_db/helpers/request_snapshot.py,sha256=Tpuu7sZ4A2Vc5e5OAyU9pSKzbOpLpZoI-4E2Ty7n4Ac,2672
|
265
|
+
stoobly_agent/app/models/factories/resource/local_db/helpers/scenario_snapshot.py,sha256=f5psNwbl_mXF8rLNogqgfiHJudu2C9N9F35DZ_zgn6o,4973
|
266
266
|
stoobly_agent/app/models/factories/resource/local_db/helpers/search.py,sha256=A7KVcmxj9c3CT2rh26YH6khiEPkB_4U1UHhiYelNaws,782
|
267
267
|
stoobly_agent/app/models/factories/resource/local_db/helpers/snapshot.py,sha256=_8WPNE2NnvLlDbqnCfgnQveppDWfFZnqCBcX6THb1AU,742
|
268
|
-
stoobly_agent/app/models/factories/resource/local_db/helpers/snapshot_service.py,sha256=
|
268
|
+
stoobly_agent/app/models/factories/resource/local_db/helpers/snapshot_service.py,sha256=szL70Vubqg2JbxZ-rnlrxiqEgyPIcKAO9SsyyFxQ6ow,2106
|
269
269
|
stoobly_agent/app/models/factories/resource/local_db/helpers/snapshot_types.py,sha256=1pX0jrg13632g_yv_sF1ABzPR7LY7r2HC9g1a5zR1qI,379
|
270
270
|
stoobly_agent/app/models/factories/resource/local_db/helpers/tiebreak_scenario_request.py,sha256=BajElzsTE115hWBT6v67eyNFnrHGz-sU6WBNCzWp7Fg,1237
|
271
271
|
stoobly_agent/app/models/factories/resource/local_db/local_db_adapter.py,sha256=Zeem6abkSHXRXTvE3XwzOkUY6fbNm9wocPP2qSX3zQ8,1439
|
@@ -287,7 +287,7 @@ stoobly_agent/app/models/factories/resource/stoobly/request_adapter.py,sha256=Zr
|
|
287
287
|
stoobly_agent/app/models/factories/resource/stoobly/scenario_adapter.py,sha256=HnM4g5Qdv16QXj8u4JCiJm2Dbw9OhAxmn9e_R8oaHG4,1105
|
288
288
|
stoobly_agent/app/models/header_model.py,sha256=m91upRZr8GfE5um0d5dguUESKigBMWhSyu_X3HFk28Y,1406
|
289
289
|
stoobly_agent/app/models/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
290
|
-
stoobly_agent/app/models/helpers/apply.py,sha256=
|
290
|
+
stoobly_agent/app/models/helpers/apply.py,sha256=ev8QmDvV7flFo82JUA6kBkofYCJ_lNXQGVIpknB94g8,8501
|
291
291
|
stoobly_agent/app/models/helpers/create_request_params_service.py,sha256=o_VB2FsTGBX55UBBw1yQ8MtsJ4-0YXcxd4kNQ9l21nM,2070
|
292
292
|
stoobly_agent/app/models/model.py,sha256=77ZTByQmH5sWBcSrCF3kG_C4muHggcFyH1DWsOhIgvg,1180
|
293
293
|
stoobly_agent/app/models/query_param_model.py,sha256=EBj76phSJ9_45KgP0vIZGbkkG6-tSn_U1fNW_7qLy_4,1455
|
@@ -688,10 +688,10 @@ stoobly_agent/test/app/cli/scenario/scenario_reset_test.py,sha256=QOyytOoFu1FALn
|
|
688
688
|
stoobly_agent/test/app/cli/scenario/scenario_snapshot_test.py,sha256=IAe1l69Smd8a9E6I0CVs8lgqnC4mI4M1EFfUjC4ZHs8,3396
|
689
689
|
stoobly_agent/test/app/cli/scenario/scenario_test_integration_test.py,sha256=BdXGe1xfb79tCCTKtp4sqI6CkZL_KamvQKLK8Wwb4u0,5543
|
690
690
|
stoobly_agent/test/app/cli/snapshot/lifecycle_hooks_migrate.py,sha256=x3x5vHfZ1xmoOpZ0yRSB3kbWbPjfbLk2PJFSa1xOLoU,316
|
691
|
-
stoobly_agent/test/app/cli/snapshot/snapshot_apply_test.py,sha256=
|
691
|
+
stoobly_agent/test/app/cli/snapshot/snapshot_apply_test.py,sha256=YIcaCxJV8CcTnCX08vq9HQyuyYf6YIwVVA3Hhx_RG2A,27752
|
692
692
|
stoobly_agent/test/app/cli/snapshot/snapshot_copy_test.py,sha256=Yg78-FhSiG_r6Jpm-sN8sn0LjVXTwTOXt6hg8ni2GIY,1953
|
693
693
|
stoobly_agent/test/app/cli/snapshot/snapshot_migrate_test.py,sha256=voEvblK6CMGCrSJDTHVmkUkLXj0auNb78jxlGiiBBQQ,7370
|
694
|
-
stoobly_agent/test/app/cli/snapshot/snapshot_prune_test.py,sha256=
|
694
|
+
stoobly_agent/test/app/cli/snapshot/snapshot_prune_test.py,sha256=G3vrXQRMoDedNpAuKgDivNMqLK-5xsCAAl80xsgeVaU,6690
|
695
695
|
stoobly_agent/test/app/cli/snapshot/snapshot_update_test.py,sha256=fILsX2M5j4wuLRP6LJTHe4CPB8gvaEbsSoYmFCHmKVk,4514
|
696
696
|
stoobly_agent/test/app/models/adapters/orm/joined_request_string_adapter_test.py,sha256=a2IHTk3l7aiLyYF7vtqissrk0MFTF2wlUBiaKWyJKfU,2667
|
697
697
|
stoobly_agent/test/app/models/adapters/orm/request/orm_mitmproxy_request_adapter_test.py,sha256=PbJsAaxPUEbF9vM7DX4z858biWf4qlGnvE8KBuy8SgY,2763
|
@@ -707,7 +707,7 @@ stoobly_agent/test/app/models/factories/resource/local_db/helpers/log_test.py,sh
|
|
707
707
|
stoobly_agent/test/app/models/factories/resource/local_db/helpers/tiebreak_scenario_request_test.py,sha256=a1SFLyEyRRLuADvAw6ckQQKORFXvyK1lyrbkaLWx8oU,3399
|
708
708
|
stoobly_agent/test/app/models/factories/resource/local_db/request_adapter_test.py,sha256=Pzq1cBPnP9oSWG-p0c-VoymoHxgp483QmNwmV1b78RA,8453
|
709
709
|
stoobly_agent/test/app/models/factories/resource/local_db/response_adapter_test.py,sha256=9P95EKH5rZGOrmRkRIDlQZqtiLJHk9735og18Ffwpfw,2204
|
710
|
-
stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=
|
710
|
+
stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=dxfL-Qxjo7CWAIdjoCBmI5kW-pkW-YJU0ao7JHCDD80,5
|
711
711
|
stoobly_agent/test/app/models/schemas/.stoobly/db/stoobly_agent.sqlite3,sha256=ch8gNx6zIelLKQx65gwFx_LRNqUD3EC5xcHZ0ukIQiU,188416
|
712
712
|
stoobly_agent/test/app/models/schemas/.stoobly/settings.yml,sha256=vLwMjweKOdod6tSLtIlyBefPQuNXq9wio4kBaODKtAU,726
|
713
713
|
stoobly_agent/test/app/models/schemas/.stoobly/tmp/options.json,sha256=OTRzarwus48CTrItedXCrgQttJHSEZonEYc7R_knvYg,2212
|
@@ -748,8 +748,8 @@ stoobly_agent/test/mock_data/scaffold/docker-compose-local-service.yml,sha256=1W
|
|
748
748
|
stoobly_agent/test/mock_data/scaffold/index.html,sha256=qJwuYajKZ4ihWZrJQ3BNObV5kf1VGnnm_vqlPJzdqLE,258
|
749
749
|
stoobly_agent/test/mock_data/uspto.yaml,sha256=6U5se7C3o-86J4m9xpOk9Npias399f5CbfWzR87WKwE,7835
|
750
750
|
stoobly_agent/test/test_helper.py,sha256=m_oAI7tmRYCNZdKfNqISWhMv3e44tjeYViQ3nTUfnos,1007
|
751
|
-
stoobly_agent-1.9.
|
752
|
-
stoobly_agent-1.9.
|
753
|
-
stoobly_agent-1.9.
|
754
|
-
stoobly_agent-1.9.
|
755
|
-
stoobly_agent-1.9.
|
751
|
+
stoobly_agent-1.9.1.dist-info/LICENSE,sha256=o93sj12cdoEOsTCjPaPFsw3Xq0SXs3pPcY-9reE2sEw,548
|
752
|
+
stoobly_agent-1.9.1.dist-info/METADATA,sha256=wlI-HxrR6hqRTUREg2Oo4NJNTsLXe7NPKrnO4mHThs0,3087
|
753
|
+
stoobly_agent-1.9.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
754
|
+
stoobly_agent-1.9.1.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
|
755
|
+
stoobly_agent-1.9.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|