fixtureqa 0.8.3__tar.gz → 0.9.1__tar.gz
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.
- {fixtureqa-0.8.3/fixtureqa.egg-info → fixtureqa-0.9.1}/PKG-INFO +2 -2
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/routers/perf.py +31 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/routers/sessions.py +1 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/schemas.py +3 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/models.py +6 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/session.py +1 -0
- fixtureqa-0.8.3/fixture/static/assets/index-Cu3H3T9B.js → fixtureqa-0.9.1/fixture/static/assets/index-CbJP_HMn.js +17 -17
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/static/index.html +1 -1
- {fixtureqa-0.8.3 → fixtureqa-0.9.1/fixtureqa.egg-info}/PKG-INFO +2 -2
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixtureqa.egg-info/SOURCES.txt +1 -1
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixtureqa.egg-info/requires.txt +1 -1
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/pyproject.toml +2 -2
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_config_store.py +1 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_non_stop_session.py +14 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_perf_api.py +21 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/LICENSE +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/README.md +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/__init__.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/__main__.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/__init__.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/app.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/connection_manager.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/deps.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/routers/__init__.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/routers/admin.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/routers/auth.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/routers/branding.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/routers/custom_tags.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/routers/fix_spec.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/routers/messages.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/routers/scenarios.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/routers/setup.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/routers/spec_overlay.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/routers/templates.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/api/routers/ws.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/config/__init__.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/__init__.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/atomic_io.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/auth.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/config_store.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/custom_tag_store.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/db_migrations.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/events.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/fix_application.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/fix_builder.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/fix_parser.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/fix_spec_parser.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/fix_tags.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/fix_time.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/housekeeping.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/inbound.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/json_store.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/latency_csv_writer.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/message_log.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/message_store.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/perf_charts.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/perf_engine.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/perf_models.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/perf_payload.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/perf_stats.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/perf_store.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/perf_writer.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/scenario_runner.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/scenario_store.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/session_manager.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/spec_overlay_store.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/template_store.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/user_store.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/value_gen.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/core/venue_responses.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/fix_specs/FIX42.xml +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/fix_specs/FIX44.xml +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/server.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/static/assets/ag-grid-_QKprVdm.js +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/static/assets/index-B-hpSZWZ.css +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/static/assets/index-CyNOPa0n.js +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/static/assets/react-vendor-2eF0YfZT.js +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/static/favicon.svg +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixture/ui/__init__.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixtureqa.egg-info/dependency_links.txt +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixtureqa.egg-info/entry_points.txt +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/fixtureqa.egg-info/top_level.txt +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/setup.cfg +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_atomic_io.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_auth.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_connection_manager.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_db_migrations.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_fix_builder.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_health.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_inbound.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_inbound_validation.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_message_store.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_perf_engine.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_perf_models.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_perf_payload.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_perf_rehydrate.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_scenarios.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_session_auto_response.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_session_lifecycle.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_session_manager_concurrency.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_sessions.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_templates.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_timezone_data.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_value_gen.py +0 -0
- {fixtureqa-0.8.3 → fixtureqa-0.9.1}/tests/test_ws.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fixtureqa
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.1
|
|
4
4
|
Summary: FIXture — FIX Protocol Testing Tool
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
License-File: LICENSE
|
|
7
|
-
Requires-Dist: fixcore-engine==0.
|
|
7
|
+
Requires-Dist: fixcore-engine==0.8.1
|
|
8
8
|
Requires-Dist: fastapi>=0.111.0
|
|
9
9
|
Requires-Dist: uvicorn[standard]>=0.29.0
|
|
10
10
|
Requires-Dist: websockets>=12.0
|
|
@@ -53,6 +53,18 @@ _TERMINAL = ("completed", "stopped", "error")
|
|
|
53
53
|
# up front (before the run starts) rather than wedging the server.
|
|
54
54
|
DURABLE_SAFE_MSG_RATE = 30 # messages/second a logged/durable session can sustain
|
|
55
55
|
|
|
56
|
+
# Session configs that add work to the per-message hot path on the event loop —
|
|
57
|
+
# they distort (or, for check_latency, can outright reject messages and corrupt)
|
|
58
|
+
# the very latency a perf run measures. Unlike durable persistence there is no
|
|
59
|
+
# rate at which they're safe, so a run that drives a session with any of them on
|
|
60
|
+
# is refused up front. (label, why) — the Performance-session toggle in the
|
|
61
|
+
# session modal clears all of them.
|
|
62
|
+
_PERF_HOSTILE_KEYS: tuple[tuple[str, str], ...] = (
|
|
63
|
+
("validate_inbound", "validates every inbound message against the spec on the loop"),
|
|
64
|
+
("check_latency", "parses SendingTime(52) and may reject messages on the loop"),
|
|
65
|
+
("enable_last_msg_seq_num_processed", "stamps LastMsgSeqNumProcessed(369) on every outbound message"),
|
|
66
|
+
)
|
|
67
|
+
|
|
56
68
|
|
|
57
69
|
def _order_rate_per_s(body: RunConfig) -> float:
|
|
58
70
|
r = body.rate
|
|
@@ -144,6 +156,25 @@ async def create_run(body: RunConfig, sm: SM, reg: REG, ts: TS, admin: Admin):
|
|
|
144
156
|
f"~{safe_order_rate:.0f} orders/s (~{DURABLE_SAFE_MSG_RATE} msg/s) — at "
|
|
145
157
|
f"higher rates the event loop stalls on per-message fsync/DB writes")
|
|
146
158
|
|
|
159
|
+
# Per-message latency guard — BEFORE reg.create(). Refuse outright if any
|
|
160
|
+
# driven session has a perf-hostile key on: these add work to the per-message
|
|
161
|
+
# hot path on the event loop (or reject messages outright) and distort the
|
|
162
|
+
# latency the run exists to measure. No safe rate, so unlike the durability
|
|
163
|
+
# guard this is a hard refusal rather than a throttle.
|
|
164
|
+
offenders = [
|
|
165
|
+
(label, attr, why)
|
|
166
|
+
for label, cfg in participating
|
|
167
|
+
for attr, why in _PERF_HOSTILE_KEYS
|
|
168
|
+
if getattr(cfg, attr, False)
|
|
169
|
+
]
|
|
170
|
+
if offenders:
|
|
171
|
+
detail = "; ".join(f"{label} session has {attr} on ({why})"
|
|
172
|
+
for label, attr, why in offenders)
|
|
173
|
+
raise HTTPException(status_code=409, detail=(
|
|
174
|
+
f"Refusing to start: {detail}. These add per-message work on the event "
|
|
175
|
+
f"loop and distort latency measurement — turn them off (or tick "
|
|
176
|
+
f"'Performance session' in the session modal) before running a perf test."))
|
|
177
|
+
|
|
147
178
|
# CompIDs from the driving (client, else venue) session — for template tokens.
|
|
148
179
|
drive_sid = body.client_session_id if need_client else body.venue_session_id
|
|
149
180
|
dcfg = sm.get_state(drive_sid).config
|
|
@@ -68,6 +68,7 @@ def _session_response(sm: SessionManager, session_id: str, us: Optional[UserStor
|
|
|
68
68
|
requires_orig_sending_time=cfg.requires_orig_sending_time,
|
|
69
69
|
resend_request_chunk_size=cfg.resend_request_chunk_size,
|
|
70
70
|
send_redundant_resend_requests=cfg.send_redundant_resend_requests,
|
|
71
|
+
enable_last_msg_seq_num_processed=cfg.enable_last_msg_seq_num_processed,
|
|
71
72
|
use_file_log=cfg.use_file_log,
|
|
72
73
|
start_time=cfg.start_time,
|
|
73
74
|
end_time=cfg.end_time,
|
|
@@ -21,6 +21,7 @@ class SessionConfigRequest(BaseModel):
|
|
|
21
21
|
requires_orig_sending_time: bool = True # RequiresOrigSendingTime
|
|
22
22
|
resend_request_chunk_size: int = Field(default=0, ge=0) # 0 = unlimited
|
|
23
23
|
send_redundant_resend_requests: bool = False
|
|
24
|
+
enable_last_msg_seq_num_processed: bool = False # stamp LastMsgSeqNumProcessed(369)
|
|
24
25
|
data_dictionary_path: Optional[str] = None
|
|
25
26
|
use_file_log: bool = True
|
|
26
27
|
start_time: str = "00:00:00"
|
|
@@ -67,6 +68,7 @@ class SessionResponse(BaseModel):
|
|
|
67
68
|
requires_orig_sending_time: bool
|
|
68
69
|
resend_request_chunk_size: int
|
|
69
70
|
send_redundant_resend_requests: bool
|
|
71
|
+
enable_last_msg_seq_num_processed: bool
|
|
70
72
|
use_file_log: bool
|
|
71
73
|
start_time: str
|
|
72
74
|
end_time: str
|
|
@@ -118,6 +120,7 @@ class UpdateSessionRequest(BaseModel):
|
|
|
118
120
|
requires_orig_sending_time: Optional[bool] = None
|
|
119
121
|
resend_request_chunk_size: Optional[int] = Field(default=None, ge=0)
|
|
120
122
|
send_redundant_resend_requests: Optional[bool] = None
|
|
123
|
+
enable_last_msg_seq_num_processed: Optional[bool] = None
|
|
121
124
|
data_dictionary_path: Optional[str] = None
|
|
122
125
|
use_file_log: Optional[bool] = None
|
|
123
126
|
start_time: Optional[str] = None
|
|
@@ -108,6 +108,12 @@ class SessionConfig:
|
|
|
108
108
|
# outstanding (default N = re-request only when the gap advances).
|
|
109
109
|
send_redundant_resend_requests: bool = False
|
|
110
110
|
|
|
111
|
+
# Message header (fixcore)
|
|
112
|
+
# Stamp LastMsgSeqNumProcessed(369) on every outbound message header, set to
|
|
113
|
+
# the seqnum of the last inbound message processed, so the peer can gauge how
|
|
114
|
+
# far behind we are (fixcore EnableLastMsgSeqNumProcessed). Off by default.
|
|
115
|
+
enable_last_msg_seq_num_processed: bool = False
|
|
116
|
+
|
|
111
117
|
# Seqnum/message store durability. True (default) = fixcore FileStore, which
|
|
112
118
|
# fsyncs per message (durable resend/recovery, but caps throughput at disk
|
|
113
119
|
# speed). False = in-memory store: no per-message fsync, far higher
|
|
@@ -382,6 +382,7 @@ class Session:
|
|
|
382
382
|
f"RequiresOrigSendingTime={yn(self.config.requires_orig_sending_time)}",
|
|
383
383
|
f"ResendRequestChunkSize={self.config.resend_request_chunk_size}",
|
|
384
384
|
f"SendRedundantResendRequests={yn(self.config.send_redundant_resend_requests)}",
|
|
385
|
+
f"EnableLastMsgSeqNumProcessed={yn(self.config.enable_last_msg_seq_num_processed)}",
|
|
385
386
|
*schedule_lines,
|
|
386
387
|
f"DataDictionary={dd_path}",
|
|
387
388
|
# Always load the dictionary so inbound repeating groups decode into
|