stoobly-agent 0.34.6__py3-none-any.whl → 0.34.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.
- stoobly_agent/__init__.py +1 -1
- stoobly_agent/app/cli/request_cli.py +1 -1
- stoobly_agent/app/cli/snapshot_cli.py +8 -0
- stoobly_agent/app/models/factories/resource/local_db/helpers/log.py +129 -27
- stoobly_agent/app/models/factories/resource/local_db/helpers/log_event.py +1 -1
- stoobly_agent/app/models/factories/resource/local_db/helpers/request_snapshot.py +4 -0
- stoobly_agent/app/models/factories/resource/local_db/helpers/scenario_snapshot.py +8 -0
- stoobly_agent/test/app/cli/request/request_snapshot_test.py +2 -2
- stoobly_agent/test/app/cli/snapshot/snapshot_apply_test.py +39 -8
- stoobly_agent/test/app/cli/snapshot/snapshot_prune_test.py +113 -0
- stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
- {stoobly_agent-0.34.6.dist-info → stoobly_agent-0.34.7.dist-info}/METADATA +1 -1
- {stoobly_agent-0.34.6.dist-info → stoobly_agent-0.34.7.dist-info}/RECORD +16 -15
- {stoobly_agent-0.34.6.dist-info → stoobly_agent-0.34.7.dist-info}/LICENSE +0 -0
- {stoobly_agent-0.34.6.dist-info → stoobly_agent-0.34.7.dist-info}/WHEEL +0 -0
- {stoobly_agent-0.34.6.dist-info → stoobly_agent-0.34.7.dist-info}/entry_points.txt +0 -0
stoobly_agent/__init__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
COMMAND = 'stoobly-agent'
|
2
|
-
VERSION = '0.34.
|
2
|
+
VERSION = '0.34.7'
|
@@ -74,6 +74,14 @@ def _list(**kwargs):
|
|
74
74
|
if len(formatted_events):
|
75
75
|
print_snapshots(formatted_events, **print_options)
|
76
76
|
|
77
|
+
@snapshot.command(
|
78
|
+
help="Prune deleted snapshots."
|
79
|
+
)
|
80
|
+
@click.option('--dry-run', is_flag=True, default=False)
|
81
|
+
def prune(**kwargs):
|
82
|
+
log = Log()
|
83
|
+
log.prune(kwargs['dry_run'])
|
84
|
+
|
77
85
|
@snapshot.command(
|
78
86
|
help="Update snapshot",
|
79
87
|
)
|
@@ -5,8 +5,10 @@ import time
|
|
5
5
|
from typing import List
|
6
6
|
|
7
7
|
from stoobly_agent.config.data_dir import DataDir
|
8
|
+
from stoobly_agent.lib.logger import bcolors, Logger
|
8
9
|
|
9
|
-
from .log_event import LogEvent
|
10
|
+
from .log_event import LogEvent
|
11
|
+
from .snapshot_types import DELETE_ACTION, PUT_ACTION, Resource
|
10
12
|
|
11
13
|
EVENT_DELIMITTER = "\n"
|
12
14
|
|
@@ -28,7 +30,7 @@ class Log():
|
|
28
30
|
if events_count == 0:
|
29
31
|
return []
|
30
32
|
|
31
|
-
return list(map(lambda raw_event: LogEvent(raw_event),
|
33
|
+
return list(map(lambda raw_event: LogEvent(raw_event), events))
|
32
34
|
|
33
35
|
@property
|
34
36
|
def history_dir_path(self):
|
@@ -56,15 +58,12 @@ class Log():
|
|
56
58
|
@property
|
57
59
|
def target_events(self):
|
58
60
|
events = self.events
|
59
|
-
return self.
|
61
|
+
return self.collapse(events)
|
60
62
|
|
61
63
|
@property
|
62
64
|
def raw_events(self):
|
63
65
|
contents = self.read()
|
64
|
-
|
65
|
-
return []
|
66
|
-
events = contents.strip().split(EVENT_DELIMITTER)
|
67
|
-
return list(filter(lambda e: not not e, events))
|
66
|
+
return self.build_raw_events(contents)
|
68
67
|
|
69
68
|
@property
|
70
69
|
def unprocessed_events(self) -> List[LogEvent]:
|
@@ -75,14 +74,11 @@ class Log():
|
|
75
74
|
return []
|
76
75
|
|
77
76
|
version = self.version.strip()
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
# Find diverge point
|
82
|
-
version_uuids = version.split(EVENT_DELIMITTER)
|
83
|
-
unprocessed_events: List[LogEvent] = self.remove_processed_events(events, version_uuids)
|
77
|
+
version_uuids = []
|
78
|
+
if version:
|
79
|
+
version_uuids = version.split(EVENT_DELIMITTER)
|
84
80
|
|
85
|
-
return self.
|
81
|
+
return self.remove_processed_events(events, version_uuids)
|
86
82
|
|
87
83
|
@property
|
88
84
|
def version(self):
|
@@ -126,6 +122,9 @@ class Log():
|
|
126
122
|
with open(self.log_file_path, 'w') as fp:
|
127
123
|
fp.write(_events + EVENT_DELIMITTER)
|
128
124
|
|
125
|
+
def build_log_events(self, raw_events) -> List[LogEvent]:
|
126
|
+
return list(map(lambda raw_event: LogEvent(raw_event), raw_events))
|
127
|
+
|
129
128
|
def next_version(self, last_processed_uuid: str = None):
|
130
129
|
uuids = self.uuids()
|
131
130
|
|
@@ -138,7 +137,7 @@ class Log():
|
|
138
137
|
|
139
138
|
return self.generate_version(uuids)
|
140
139
|
|
141
|
-
def
|
140
|
+
def collapse(self, events: List[LogEvent]) -> List[LogEvent]:
|
142
141
|
events_count = {}
|
143
142
|
|
144
143
|
# More recent events take precedence over earlier ones, keep only the most recent event
|
@@ -164,6 +163,62 @@ class Log():
|
|
164
163
|
serialized_event = LogEvent.serialize_put(resource)
|
165
164
|
self.append(serialized_event)
|
166
165
|
|
166
|
+
def prune(self, dry_run = False):
|
167
|
+
# event uuid => history path
|
168
|
+
events: List[LogEvent] = []
|
169
|
+
path_index = {}
|
170
|
+
|
171
|
+
history_files = self.history_files
|
172
|
+
for file_path in history_files:
|
173
|
+
with open(file_path, 'r') as fp:
|
174
|
+
contents = fp.read().strip()
|
175
|
+
raw_events = self.build_raw_events(contents)
|
176
|
+
history_events = self.build_log_events(raw_events)
|
177
|
+
|
178
|
+
for event in history_events:
|
179
|
+
path_index[event.uuid] = file_path
|
180
|
+
|
181
|
+
events += history_events
|
182
|
+
|
183
|
+
# resource_uuid => event
|
184
|
+
resource_index = {}
|
185
|
+
for event in events:
|
186
|
+
if event.resource_uuid not in resource_index:
|
187
|
+
resource_index[event.resource_uuid] = []
|
188
|
+
|
189
|
+
resource_index[event.resource_uuid].append(event)
|
190
|
+
|
191
|
+
pruned_events = self.collapse(events)
|
192
|
+
for event in pruned_events:
|
193
|
+
snapshot = event.snapshot()
|
194
|
+
snapshot_exists = snapshot.exists
|
195
|
+
|
196
|
+
if event.action == DELETE_ACTION or not snapshot_exists:
|
197
|
+
Logger.instance().info(f"{bcolors.OKBLUE}Removing {event.resource} {event.resource_uuid}{bcolors.ENDC}")
|
198
|
+
|
199
|
+
resource_events: List[LogEvent] = resource_index[event.resource_uuid]
|
200
|
+
removed_events = {}
|
201
|
+
|
202
|
+
for event in resource_events:
|
203
|
+
history_path = path_index[event.uuid]
|
204
|
+
if event.uuid in removed_events:
|
205
|
+
continue
|
206
|
+
|
207
|
+
Logger.instance().info(f"Removing event {event.uuid}")
|
208
|
+
self.remove_event_history(event, history_path, dry_run)
|
209
|
+
removed_events[event.uuid] = True
|
210
|
+
|
211
|
+
if event.action == DELETE_ACTION and snapshot_exists:
|
212
|
+
if not dry_run:
|
213
|
+
snapshot.remove()
|
214
|
+
Logger.instance().info(f"Removing {event.resource} snapshot")
|
215
|
+
|
216
|
+
def build_raw_events(self, contents: str) -> List[str]:
|
217
|
+
if not contents:
|
218
|
+
return []
|
219
|
+
events = contents.strip().split(EVENT_DELIMITTER)
|
220
|
+
return list(filter(lambda e: not not e, events))
|
221
|
+
|
167
222
|
def read(self):
|
168
223
|
history_files = self.history_files
|
169
224
|
|
@@ -173,18 +228,56 @@ class Log():
|
|
173
228
|
log.append(fp.read().strip())
|
174
229
|
|
175
230
|
return EVENT_DELIMITTER.join(log)
|
176
|
-
|
177
|
-
def
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
231
|
+
|
232
|
+
def remove_dangling_events(self, processed_events: List[LogEvent], unprocessed_events: List[LogEvent]):
|
233
|
+
'''
|
234
|
+
Remove DELETE events where the last processed event was a PUT
|
235
|
+
'''
|
236
|
+
index = {}
|
237
|
+
for event in processed_events:
|
238
|
+
if event.action == PUT_ACTION:
|
239
|
+
index[event.resource_uuid] = event
|
240
|
+
elif event.action == DELETE_ACTION:
|
241
|
+
if event.resource_uuid in index:
|
242
|
+
del index[event.resource_uuid]
|
243
|
+
|
244
|
+
return list(
|
245
|
+
filter(
|
246
|
+
lambda e: e.action != DELETE_ACTION or (e.action == DELETE_ACTION and e.resource_uuid in index),
|
247
|
+
unprocessed_events
|
248
|
+
)
|
249
|
+
)
|
250
|
+
|
251
|
+
def remove_event_history(self, event: LogEvent, history_path: str, dry_run = False):
|
252
|
+
events = []
|
253
|
+
raw_events = []
|
254
|
+
|
255
|
+
with open(history_path, 'r') as fp:
|
256
|
+
contents = fp.read().strip()
|
257
|
+
raw_events = self.build_raw_events(contents)
|
258
|
+
events = self.build_log_events(raw_events)
|
259
|
+
events = list(filter(lambda log_event: log_event.uuid != event.uuid, events))
|
260
|
+
|
261
|
+
if len(events) == 0:
|
262
|
+
Logger.instance().info(f"Removing {history_path}")
|
263
|
+
|
264
|
+
if not dry_run:
|
265
|
+
os.remove(history_path)
|
266
|
+
else:
|
267
|
+
new_raw_events = list(map(lambda event: str(event), events))
|
268
|
+
Logger.instance().info(f"Updating {history_path}, Events: {len(raw_events)} -> {len(new_raw_events)}")
|
269
|
+
|
270
|
+
if not dry_run:
|
271
|
+
with open(history_path, 'w') as fp:
|
272
|
+
fp.write(EVENT_DELIMITTER.join(new_raw_events))
|
273
|
+
|
274
|
+
def remove_processed_events(self, events, version_uuids):
|
275
|
+
i = self.__diverge_point(events, version_uuids)
|
276
|
+
processed_events = self.collapse(events[0:i])
|
277
|
+
unprocessed_events = self.collapse(events[i:])
|
278
|
+
remaining_version_uuids = version_uuids[i:]
|
279
|
+
|
280
|
+
remaining_events = self.remove_dangling_events(processed_events, unprocessed_events)
|
188
281
|
return list(filter(lambda e: e.uuid not in remaining_version_uuids, remaining_events))
|
189
282
|
|
190
283
|
# Rotate log to history
|
@@ -201,6 +294,15 @@ class Log():
|
|
201
294
|
_events = events or self.events
|
202
295
|
return list(map(lambda e: e.uuid, _events))
|
203
296
|
|
297
|
+
def __diverge_point(self, events: List[LogEvent], version_uuids: List[str]):
|
298
|
+
iterations = min(len(events), len(version_uuids))
|
299
|
+
for i in range(0, iterations):
|
300
|
+
event = events[i]
|
301
|
+
|
302
|
+
if event.uuid != version_uuids[i]:
|
303
|
+
return i
|
304
|
+
return iterations
|
305
|
+
|
204
306
|
def __history_file_path(self, bucket_interval: int):
|
205
307
|
file_name = f"{int(time.time() / bucket_interval) * bucket_interval}"
|
206
308
|
return os.path.join(self.history_dir_path, file_name)
|
@@ -20,6 +20,10 @@ class ScenarioSnapshot(Snapshot):
|
|
20
20
|
self.__metadata_backup = None
|
21
21
|
self.__requests_backup = None
|
22
22
|
|
23
|
+
@property
|
24
|
+
def exists(self):
|
25
|
+
return os.path.exists(self.requests_path) and os.path.exists(self.metadata_path)
|
26
|
+
|
23
27
|
@property
|
24
28
|
def metadata(self):
|
25
29
|
if not os.path.exists(self.metadata_path):
|
@@ -84,6 +88,10 @@ class ScenarioSnapshot(Snapshot):
|
|
84
88
|
request_snapshot = RequestSnapshot(uuid)
|
85
89
|
handler(request_snapshot)
|
86
90
|
|
91
|
+
def remove(self):
|
92
|
+
self.remove_metadata()
|
93
|
+
self.remove_requests()
|
94
|
+
|
87
95
|
def remove_metadata(self):
|
88
96
|
metadata_file_path = self.metadata_path
|
89
97
|
|
@@ -77,7 +77,7 @@ class TestRequestSnapshot():
|
|
77
77
|
assert len(events) == 3
|
78
78
|
|
79
79
|
unprocessed_events = log.unprocessed_events
|
80
|
-
assert len(unprocessed_events) ==
|
80
|
+
assert len(unprocessed_events) == 0
|
81
81
|
|
82
82
|
class TestWhenAppending():
|
83
83
|
|
@@ -118,7 +118,7 @@ class TestRequestSnapshot():
|
|
118
118
|
assert len(events) == 3
|
119
119
|
|
120
120
|
unprocessed_events = log.unprocessed_events
|
121
|
-
assert len(unprocessed_events) ==
|
121
|
+
assert len(unprocessed_events) == 1
|
122
122
|
|
123
123
|
event = unprocessed_events[0]
|
124
124
|
assert event.resource_uuid == recorded_request_two.uuid
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import pdb
|
2
2
|
import pytest
|
3
|
+
import time
|
3
4
|
|
4
5
|
from click.testing import CliRunner
|
5
6
|
from typing import List
|
@@ -23,6 +24,10 @@ class TestApply():
|
|
23
24
|
|
24
25
|
class TestWhenDeletingRequest():
|
25
26
|
|
27
|
+
@pytest.fixture(scope='class')
|
28
|
+
def log(self):
|
29
|
+
return Log()
|
30
|
+
|
26
31
|
@pytest.fixture(scope='class')
|
27
32
|
def recorded_request(self, runner: CliRunner):
|
28
33
|
record_result = runner.invoke(record, [DETERMINISTIC_GET_REQUEST_URL])
|
@@ -30,13 +35,24 @@ class TestApply():
|
|
30
35
|
return Request.last()
|
31
36
|
|
32
37
|
@pytest.fixture(autouse=True, scope='class')
|
33
|
-
def
|
34
|
-
snapshot_result = runner.invoke(request, ['snapshot',
|
38
|
+
def events(self, runner: CliRunner, recorded_request: Request, log: Log):
|
39
|
+
snapshot_result = runner.invoke(request, ['snapshot', recorded_request.key()])
|
35
40
|
assert snapshot_result.exit_code == 0
|
36
41
|
|
37
|
-
log = Log()
|
38
42
|
events = log.events
|
39
|
-
|
43
|
+
event: LogEvent = events[len(events) - 1]
|
44
|
+
log.version = event.uuid
|
45
|
+
|
46
|
+
time.sleep(1)
|
47
|
+
|
48
|
+
snapshot_result = runner.invoke(request, ['snapshot', '--action', DELETE_ACTION, recorded_request.key()])
|
49
|
+
assert snapshot_result.exit_code == 0
|
50
|
+
|
51
|
+
return log.events
|
52
|
+
|
53
|
+
@pytest.fixture(scope='class')
|
54
|
+
def delete_event(self, events: List[LogEvent]):
|
55
|
+
return events[1]
|
40
56
|
|
41
57
|
def test_it_deletes(self, runner: CliRunner, recorded_request: Request, delete_event: LogEvent):
|
42
58
|
apply_result = runner.invoke(snapshot, ['apply', '--force', delete_event.uuid])
|
@@ -82,6 +98,10 @@ class TestApply():
|
|
82
98
|
|
83
99
|
class TestWhenDeletingScenario():
|
84
100
|
|
101
|
+
@pytest.fixture(scope='class')
|
102
|
+
def log(self):
|
103
|
+
return Log()
|
104
|
+
|
85
105
|
@pytest.fixture(scope='class')
|
86
106
|
def created_scenario(self, runner: CliRunner):
|
87
107
|
create_result = runner.invoke(scenario, ['create', 'test'])
|
@@ -89,13 +109,24 @@ class TestApply():
|
|
89
109
|
return Scenario.last()
|
90
110
|
|
91
111
|
@pytest.fixture(autouse=True, scope='class')
|
92
|
-
def
|
93
|
-
snapshot_result = runner.invoke(scenario, ['snapshot',
|
112
|
+
def events(self, runner: CliRunner, created_scenario: Scenario, log: Log):
|
113
|
+
snapshot_result = runner.invoke(scenario, ['snapshot', created_scenario.key()])
|
94
114
|
assert snapshot_result.exit_code == 0
|
95
115
|
|
96
|
-
log = Log()
|
97
116
|
events = log.events
|
98
|
-
|
117
|
+
event: LogEvent = events[len(events) - 1]
|
118
|
+
log.version = event.uuid
|
119
|
+
|
120
|
+
time.sleep(1)
|
121
|
+
|
122
|
+
snapshot_result = runner.invoke(scenario, ['snapshot', '--action', DELETE_ACTION, created_scenario.key()])
|
123
|
+
assert snapshot_result.exit_code == 0
|
124
|
+
|
125
|
+
return log.events
|
126
|
+
|
127
|
+
@pytest.fixture(scope='class')
|
128
|
+
def delete_event(self, events):
|
129
|
+
return events[1]
|
99
130
|
|
100
131
|
def test_it_deletes(self, runner: CliRunner, created_scenario: Scenario, delete_event: LogEvent):
|
101
132
|
snapshot_result = runner.invoke(snapshot, ['apply', '--force', delete_event.uuid])
|
@@ -0,0 +1,113 @@
|
|
1
|
+
import pdb
|
2
|
+
import pytest
|
3
|
+
import time
|
4
|
+
|
5
|
+
from click.testing import CliRunner
|
6
|
+
from typing import List
|
7
|
+
|
8
|
+
from stoobly_agent.app.models.factories.resource.local_db.helpers.log import Log
|
9
|
+
from stoobly_agent.app.models.factories.resource.local_db.helpers.log_event import DELETE_ACTION, LogEvent
|
10
|
+
from stoobly_agent.app.models.factories.resource.local_db.helpers.scenario_snapshot import ScenarioSnapshot
|
11
|
+
from stoobly_agent.cli import record, request, scenario, snapshot
|
12
|
+
from stoobly_agent.lib.orm.request import Request
|
13
|
+
from stoobly_agent.lib.orm.scenario import Scenario
|
14
|
+
from stoobly_agent.test.test_helper import assert_orm_request_equivalent, DETERMINISTIC_GET_REQUEST_URL, NON_DETERMINISTIC_GET_REQUEST_URL, reset
|
15
|
+
|
16
|
+
@pytest.fixture(scope='module')
|
17
|
+
def runner():
|
18
|
+
return CliRunner()
|
19
|
+
|
20
|
+
class TestPrune():
|
21
|
+
@pytest.fixture(scope='class', autouse=True)
|
22
|
+
def settings(self):
|
23
|
+
return reset()
|
24
|
+
|
25
|
+
class TestWhenDeletingRequest():
|
26
|
+
|
27
|
+
@pytest.fixture(scope='class')
|
28
|
+
def log(self):
|
29
|
+
return Log()
|
30
|
+
|
31
|
+
@pytest.fixture(scope='class')
|
32
|
+
def recorded_request(self, runner: CliRunner):
|
33
|
+
record_result = runner.invoke(record, [DETERMINISTIC_GET_REQUEST_URL])
|
34
|
+
assert record_result.exit_code == 0
|
35
|
+
return Request.last()
|
36
|
+
|
37
|
+
@pytest.fixture(autouse=True, scope='class')
|
38
|
+
def events(self, runner: CliRunner, recorded_request: Request, log: Log):
|
39
|
+
snapshot_result = runner.invoke(request, ['snapshot', recorded_request.key()])
|
40
|
+
assert snapshot_result.exit_code == 0
|
41
|
+
|
42
|
+
events = log.events
|
43
|
+
event: LogEvent = events[len(events) - 1]
|
44
|
+
log.version = event.uuid
|
45
|
+
|
46
|
+
time.sleep(1)
|
47
|
+
|
48
|
+
snapshot_result = runner.invoke(request, ['snapshot', '--action', DELETE_ACTION, recorded_request.key()])
|
49
|
+
assert snapshot_result.exit_code == 0
|
50
|
+
|
51
|
+
return log.events
|
52
|
+
|
53
|
+
def test_it_prunes(self, runner: CliRunner, log: Log):
|
54
|
+
assert len(log.events) == 2
|
55
|
+
apply_result = runner.invoke(snapshot, ['prune'])
|
56
|
+
assert apply_result.exit_code == 0
|
57
|
+
assert len(log.events) == 0
|
58
|
+
|
59
|
+
class TestWhenCreatingRequest():
|
60
|
+
|
61
|
+
@pytest.fixture(scope='class')
|
62
|
+
def log(self):
|
63
|
+
return Log()
|
64
|
+
|
65
|
+
@pytest.fixture(scope='function')
|
66
|
+
def recorded_request(self, runner: CliRunner):
|
67
|
+
record_result = runner.invoke(record, [DETERMINISTIC_GET_REQUEST_URL])
|
68
|
+
assert record_result.exit_code == 0
|
69
|
+
return Request.last()
|
70
|
+
|
71
|
+
@pytest.fixture(autouse=True, scope='function')
|
72
|
+
def put_event(self, runner: CliRunner, recorded_request: Request):
|
73
|
+
snapshot_result = runner.invoke(request, ['snapshot', recorded_request.key()])
|
74
|
+
assert snapshot_result.exit_code == 0
|
75
|
+
|
76
|
+
log = Log()
|
77
|
+
events = log.events
|
78
|
+
return events[len(events) - 1]
|
79
|
+
|
80
|
+
def test_it_does_not_prune(self, runner: CliRunner, log: Log):
|
81
|
+
apply_result = runner.invoke(snapshot, ['prune'])
|
82
|
+
assert apply_result.exit_code == 0
|
83
|
+
assert len(log.events) == 1
|
84
|
+
|
85
|
+
class TestWhenMissingSnapshot():
|
86
|
+
|
87
|
+
@pytest.fixture(scope='class')
|
88
|
+
def log(self):
|
89
|
+
return Log()
|
90
|
+
|
91
|
+
@pytest.fixture(scope='function')
|
92
|
+
def recorded_request(self, runner: CliRunner):
|
93
|
+
record_result = runner.invoke(record, [DETERMINISTIC_GET_REQUEST_URL])
|
94
|
+
assert record_result.exit_code == 0
|
95
|
+
return Request.last()
|
96
|
+
|
97
|
+
@pytest.fixture(autouse=True, scope='function')
|
98
|
+
def put_event(self, runner: CliRunner, recorded_request: Request):
|
99
|
+
snapshot_result = runner.invoke(request, ['snapshot', recorded_request.key()])
|
100
|
+
assert snapshot_result.exit_code == 0
|
101
|
+
|
102
|
+
log = Log()
|
103
|
+
events = log.events
|
104
|
+
return events[len(events) - 1]
|
105
|
+
|
106
|
+
def test_it_prunes(self, runner: CliRunner, log: Log):
|
107
|
+
event = log.events[0]
|
108
|
+
event.snapshot().remove()
|
109
|
+
assert len(log.events) == 1
|
110
|
+
|
111
|
+
apply_result = runner.invoke(snapshot, ['prune'])
|
112
|
+
assert apply_result.exit_code == 0
|
113
|
+
assert len(log.events) == 0
|
@@ -1 +1 @@
|
|
1
|
-
0.34.
|
1
|
+
0.34.6
|
@@ -1,4 +1,4 @@
|
|
1
|
-
stoobly_agent/__init__.py,sha256=
|
1
|
+
stoobly_agent/__init__.py,sha256=YRhtJ34t0WtULrjMrEGKhizGdQGPnijkWflQA-KjphE,45
|
2
2
|
stoobly_agent/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
stoobly_agent/app/api/__init__.py,sha256=FFSlVoTgjPfUNlYPr_7u6-P5Y4WOKyaUfAHtUcB-Xio,810
|
4
4
|
stoobly_agent/app/api/application_http_request_handler.py,sha256=jf4fkqjOiCeI2IM5Ro7ie0v_C6y0-7-5TIE_IKMPOfg,5513
|
@@ -64,9 +64,9 @@ stoobly_agent/app/cli/intercept_cli.py,sha256=jYbmH9FUI6J9vTlC4ycwRRNVYk0GRC8ZI5
|
|
64
64
|
stoobly_agent/app/cli/main_group.py,sha256=kHR-0P9QL2No4XG1asgDfisIDdfxgLbj30y3vEobClQ,2175
|
65
65
|
stoobly_agent/app/cli/project_cli.py,sha256=EXjeLjbnq9PhfCjvyfZ0UnJ2tejeCS0SIAo3Nc4fKOc,3852
|
66
66
|
stoobly_agent/app/cli/report_cli.py,sha256=ZxJw0Xkx7KFZJn9e45BSKRKon8AD0Msrwy1fbPfbv0c,2543
|
67
|
-
stoobly_agent/app/cli/request_cli.py,sha256=
|
67
|
+
stoobly_agent/app/cli/request_cli.py,sha256=THNloW111l9MLE0oPg4y7hVXL1U7OXoz760g0A1CtJ0,7747
|
68
68
|
stoobly_agent/app/cli/scenario_cli.py,sha256=3J1EiJOvunkfWrEkOsanw-XrKkOk78ij_GjBlE9p7CE,8229
|
69
|
-
stoobly_agent/app/cli/snapshot_cli.py,sha256=
|
69
|
+
stoobly_agent/app/cli/snapshot_cli.py,sha256=1AvCNiw9KyCejcftjdKozmxK3f2EE1caHuMeANdmuT4,7756
|
70
70
|
stoobly_agent/app/cli/trace_cli.py,sha256=K7E-vx3JUcqEDSWOdIOi_AieKNQz7dBfmRrVvKDkzFI,4605
|
71
71
|
stoobly_agent/app/cli/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
72
72
|
stoobly_agent/app/cli/types/output.py,sha256=2wazv56g5IwLQeJCWfJXXAxTB9Y5WH1cKMHbpjbXNeo,439
|
@@ -118,11 +118,11 @@ stoobly_agent/app/models/factories/resource/local_db/body_adapter.py,sha256=lrnI
|
|
118
118
|
stoobly_agent/app/models/factories/resource/local_db/header_adapter.py,sha256=gIhWeoSPCK7gjL6CQFi39zYqkd38Lb1fVKhzW5DJSeE,2640
|
119
119
|
stoobly_agent/app/models/factories/resource/local_db/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
120
120
|
stoobly_agent/app/models/factories/resource/local_db/helpers/create_request_columns_service.py,sha256=HABMelW-cvhm2WXaywbQcd4PQhzSrz4vAbN7uOdetXM,1541
|
121
|
-
stoobly_agent/app/models/factories/resource/local_db/helpers/log.py,sha256=
|
122
|
-
stoobly_agent/app/models/factories/resource/local_db/helpers/log_event.py,sha256=
|
121
|
+
stoobly_agent/app/models/factories/resource/local_db/helpers/log.py,sha256=quuL1YoElqqeKk1tpSaxYbMyOdrWQyHnrM_KiT27S6w,8909
|
122
|
+
stoobly_agent/app/models/factories/resource/local_db/helpers/log_event.py,sha256=vYZ-tgl5brQREj2eQ9LPsWfwDIXN45ip6VE1ZZBKc10,3364
|
123
123
|
stoobly_agent/app/models/factories/resource/local_db/helpers/request_builder.py,sha256=PyVvsYmi5bBQ6PsUPxQ4nJM9rhhjGVOTd7ipuc2tMMM,3227
|
124
|
-
stoobly_agent/app/models/factories/resource/local_db/helpers/request_snapshot.py,sha256=
|
125
|
-
stoobly_agent/app/models/factories/resource/local_db/helpers/scenario_snapshot.py,sha256=
|
124
|
+
stoobly_agent/app/models/factories/resource/local_db/helpers/request_snapshot.py,sha256=u2sgQrhLJNbrsi1FMXFRZn8Gj8zYnmn3WizyhR3bHeo,1710
|
125
|
+
stoobly_agent/app/models/factories/resource/local_db/helpers/scenario_snapshot.py,sha256=gpn4F26cSJiry93dECDqlS8PLIkN9Gh0nLgGztlfgsU,3845
|
126
126
|
stoobly_agent/app/models/factories/resource/local_db/helpers/search.py,sha256=A7KVcmxj9c3CT2rh26YH6khiEPkB_4U1UHhiYelNaws,782
|
127
127
|
stoobly_agent/app/models/factories/resource/local_db/helpers/snapshot.py,sha256=4w3sHiG5F6gUdMfNTm5EfPrHJ-ah00KkJshypAIbmm0,312
|
128
128
|
stoobly_agent/app/models/factories/resource/local_db/helpers/snapshot_service.py,sha256=fbqh5J5sMj0n_YFQFei2xhXLrSGU9vNmSX_uifA3U3M,1671
|
@@ -529,7 +529,7 @@ stoobly_agent/test/app/cli/request/request_list_test.py,sha256=tqMjkODU2i2eDvYyk
|
|
529
529
|
stoobly_agent/test/app/cli/request/request_replay_test.py,sha256=w13NzkXhmBKvoogpQ8CdmBbCzoyqxEQvBZsEkGDCPx0,6237
|
530
530
|
stoobly_agent/test/app/cli/request/request_reset_test.py,sha256=5My6Z452eideAOUun77tWUkuu3_yGhDVxCG1baUF8Zo,1290
|
531
531
|
stoobly_agent/test/app/cli/request/request_response_test.py,sha256=Fu-A8tIn016DKme4WIaPzo3YeFY-CPtTOpaSFigUVVM,1263
|
532
|
-
stoobly_agent/test/app/cli/request/request_snapshot_test.py,sha256=
|
532
|
+
stoobly_agent/test/app/cli/request/request_snapshot_test.py,sha256=0013aoiMZin-2YEtHzEmQspAPA3SUd_6XiItbX0U7Ok,4425
|
533
533
|
stoobly_agent/test/app/cli/request/request_test_test.py,sha256=-cJNXKjgryVVfVt-7IN5fIhBwe3NjFoPmeavDH8lAjU,5527
|
534
534
|
stoobly_agent/test/app/cli/scenario/scenario_create_test.py,sha256=fGqcjO1_1OvdpUMQfGRVkSyFe61u8WIcp_ndLFrf33A,3962
|
535
535
|
stoobly_agent/test/app/cli/scenario/scenario_replay_integration_test.py,sha256=NbGJzmvPsNLBR0ac65yt_cOTfpnsST1IG7i3F0euwAk,7031
|
@@ -537,7 +537,8 @@ stoobly_agent/test/app/cli/scenario/scenario_replay_test.py,sha256=XqR-GDrR8uUtx
|
|
537
537
|
stoobly_agent/test/app/cli/scenario/scenario_reset_test.py,sha256=QOyytOoFu1FALny3-xwp9r4bfOE9fftgnhkDJWYxXh0,2021
|
538
538
|
stoobly_agent/test/app/cli/scenario/scenario_snapshot_test.py,sha256=vk572Oxp1c8Y4utqq3L24nTkFj5Fttig0R2cWXUzg3E,3395
|
539
539
|
stoobly_agent/test/app/cli/scenario/scenario_test_integration_test.py,sha256=BdXGe1xfb79tCCTKtp4sqI6CkZL_KamvQKLK8Wwb4u0,5543
|
540
|
-
stoobly_agent/test/app/cli/snapshot/snapshot_apply_test.py,sha256=
|
540
|
+
stoobly_agent/test/app/cli/snapshot/snapshot_apply_test.py,sha256=gyNWX9b4PFkmxQxEth8xe1-xNryvjBuM0AyuJsEPOQA,15529
|
541
|
+
stoobly_agent/test/app/cli/snapshot/snapshot_prune_test.py,sha256=221I9q-1L4qDE-CnvPO2eRCFphrbjedx5-KhRpaPQig,4012
|
541
542
|
stoobly_agent/test/app/cli/snapshot/snapshot_update_test.py,sha256=1SBwY4uijn3UxSUHNafbj0leX6kIeb9bd3f_AqKLZxs,4538
|
542
543
|
stoobly_agent/test/app/models/adapters/orm/joined_request_string_adapter_test.py,sha256=a2IHTk3l7aiLyYF7vtqissrk0MFTF2wlUBiaKWyJKfU,2667
|
543
544
|
stoobly_agent/test/app/models/adapters/orm/request/orm_mitmproxy_request_adapter_test.py,sha256=PbJsAaxPUEbF9vM7DX4z858biWf4qlGnvE8KBuy8SgY,2763
|
@@ -553,7 +554,7 @@ stoobly_agent/test/app/models/factories/resource/local_db/helpers/log_test.py,sh
|
|
553
554
|
stoobly_agent/test/app/models/factories/resource/local_db/helpers/tiebreak_scenario_request_test.py,sha256=Ft5POn1ZJqt3YOSdPOb0PwwCaIkBEpw3Jug9WAeAt-g,2863
|
554
555
|
stoobly_agent/test/app/models/factories/resource/local_db/request_adapter_test.py,sha256=Pzq1cBPnP9oSWG-p0c-VoymoHxgp483QmNwmV1b78RA,8453
|
555
556
|
stoobly_agent/test/app/models/factories/resource/local_db/response_adapter_test.py,sha256=9P95EKH5rZGOrmRkRIDlQZqtiLJHk9735og18Ffwpfw,2204
|
556
|
-
stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=
|
557
|
+
stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=E9YELIgwnEFxYMI--zJNcBtpkH9G7fPbJrBsKEXnYRU,6
|
557
558
|
stoobly_agent/test/app/models/schemas/.stoobly/db/stoobly_agent.sqlite3,sha256=ch8gNx6zIelLKQx65gwFx_LRNqUD3EC5xcHZ0ukIQiU,188416
|
558
559
|
stoobly_agent/test/app/models/schemas/.stoobly/settings.yml,sha256=vLwMjweKOdod6tSLtIlyBefPQuNXq9wio4kBaODKtAU,726
|
559
560
|
stoobly_agent/test/app/models/schemas/.stoobly/tmp/options.json,sha256=OTRzarwus48CTrItedXCrgQttJHSEZonEYc7R_knvYg,2212
|
@@ -587,8 +588,8 @@ stoobly_agent/test/mock_data/petstore.yaml,sha256=CCdliJky04Az4FIOkFA883uunwFDHL
|
|
587
588
|
stoobly_agent/test/mock_data/request_show_response.py,sha256=K_a0fP0QT58T8sX9PaM6hqtX1A1depZsqg_GsNPf--k,707
|
588
589
|
stoobly_agent/test/mock_data/uspto.yaml,sha256=6U5se7C3o-86J4m9xpOk9Npias399f5CbfWzR87WKwE,7835
|
589
590
|
stoobly_agent/test/test_helper.py,sha256=m_oAI7tmRYCNZdKfNqISWhMv3e44tjeYViQ3nTUfnos,1007
|
590
|
-
stoobly_agent-0.34.
|
591
|
-
stoobly_agent-0.34.
|
592
|
-
stoobly_agent-0.34.
|
593
|
-
stoobly_agent-0.34.
|
594
|
-
stoobly_agent-0.34.
|
591
|
+
stoobly_agent-0.34.7.dist-info/LICENSE,sha256=8QKGyy45eN76Zk52h8gu1DKX2B_gbWgZ3nzDLofEbaE,548
|
592
|
+
stoobly_agent-0.34.7.dist-info/METADATA,sha256=pWqiDODcIgUwr_58gRyWrBY3BIHa3nxGIHi4cDpiC8Y,3304
|
593
|
+
stoobly_agent-0.34.7.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
594
|
+
stoobly_agent-0.34.7.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
|
595
|
+
stoobly_agent-0.34.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|