@@ -44,6 +44,15 @@ def getcode(self):
4444
4545
4646class M0ResearchPublisherEnvelopeTest (unittest .TestCase ):
47+ def test_schema_declares_the_cross_module_canonical_utf8_body_limit (self ):
48+ schema = json .loads (
49+ (ROOT .parent / "schemas" / "qsl-m0-research-publisher-envelope.v1.schema.json" ).read_text (
50+ encoding = "utf-8"
51+ )
52+ )
53+ self .assertEqual (schema ["x-qsl-canonical-utf8-max-bytes" ], 256 * 1024 )
54+ self .assertIn ("canonical UTF-8 JSON request body" , schema ["$comment" ])
55+
4756 def _snapshot (self ) -> dict [str , object ]:
4857 return {
4958 "schema_version" : "qsl_m0_research_source_snapshot.v1" ,
@@ -147,6 +156,28 @@ def test_build_is_deterministic_hash_bound_and_research_only(self):
147156 self .assertTrue (first ["ledger" ]["policy" ]["no_order" ])
148157 self .assertEqual (first ["ledger_sha256" ], publisher .calculate_ledger_sha256 (first ["ledger" ]))
149158 self .assertEqual (publisher .validate_m0_research_publisher_envelope (first ), first )
159+ self .assertLessEqual (
160+ len (publisher .canonical_envelope_body (first )),
161+ publisher .MAX_PUBLISHER_ENVELOPE_BYTES ,
162+ )
163+
164+ def test_builder_fails_closed_when_actual_utf8_envelope_body_exceeds_worker_ingress_limit (self ):
165+ oversized = self ._snapshot ()
166+ hypotheses = []
167+ for index in range (500 ):
168+ hypothesis = json .loads (json .dumps (oversized ["hypotheses" ][0 ]))
169+ hypothesis ["hypothesis_id" ] = f"m0r-large-{ index :03d} "
170+ hypothesis ["subject" ]["identifier" ] = f"SOXX-{ index :03d} "
171+ hypotheses .append (hypothesis )
172+ oversized ["hypotheses" ] = hypotheses
173+ with self .assertRaisesRegex (publisher .M0ResearchPublisherEnvelopeError , "publisher_envelope_size_exceeded" ):
174+ publisher .build_m0_research_publisher_envelope (
175+ source_snapshot = oversized ,
176+ source_artifact = self ._artifact ("f" * 64 ),
177+ producer_repository = "QuantStrategyLab/QuantRuntimeSettings" ,
178+ producer_revision = "e" * 40 ,
179+ now = "2026-08-21T12:00:00Z" ,
180+ )
150181
151182 def test_envelope_validation_rejects_digest_or_execution_policy_tampering (self ):
152183 envelope = publisher .build_m0_research_publisher_envelope (
@@ -180,12 +211,37 @@ def test_cli_default_is_local_only_and_binds_the_exact_source_bytes(self):
180211 envelope = json .loads (output .read_text (encoding = "utf-8" ))
181212 self .assertEqual (envelope ["source_artifact" ]["sha256" ], sha256 )
182213 self .assertEqual (envelope ["ledger_sha256" ], publisher .calculate_ledger_sha256 (envelope ["ledger" ]))
214+ self .assertEqual (output .read_bytes (), publisher .canonical_envelope_body (envelope ) + b"\n " )
183215
184216 missing_output = root / "missing.json"
185217 with self .assertRaisesRegex (publisher .M0ResearchPublisherEnvelopeError , "source_artifact_sha256_mismatch" ):
186218 publisher .main (self ._arguments (source , missing_output , "0" * 64 ))
187219 self .assertFalse (missing_output .exists ())
188220
221+ def test_cli_oversize_fails_before_any_write_or_opt_in_publish (self ):
222+ oversized = self ._snapshot ()
223+ hypotheses = []
224+ for index in range (500 ):
225+ hypothesis = json .loads (json .dumps (oversized ["hypotheses" ][0 ]))
226+ hypothesis ["hypothesis_id" ] = f"m0r-large-{ index :03d} "
227+ hypothesis ["subject" ]["identifier" ] = f"SOXX-{ index :03d} "
228+ hypotheses .append (hypothesis )
229+ oversized ["hypotheses" ] = hypotheses
230+ with tempfile .TemporaryDirectory () as temp_dir :
231+ root = Path (temp_dir )
232+ source = root / "oversized-source.json"
233+ output = root / "must-not-exist.json"
234+ raw = json .dumps (oversized , ensure_ascii = False , separators = ("," , ":" )).encode ("utf-8" )
235+ source .write_bytes (raw )
236+ arguments = self ._arguments (source , output , hashlib .sha256 (raw ).hexdigest ()) + ["--publish" ]
237+ with patch .object (publisher .urllib .request , "urlopen" , side_effect = AssertionError ("network called" )):
238+ with self .assertRaisesRegex (
239+ publisher .M0ResearchPublisherEnvelopeError ,
240+ "publisher_envelope_size_exceeded" ,
241+ ):
242+ publisher .main (arguments )
243+ self .assertFalse (output .exists ())
244+
189245 def test_publish_requires_dedicated_environment_and_never_serializes_token (self ):
190246 envelope = publisher .build_m0_research_publisher_envelope (
191247 source_snapshot = self ._snapshot (),
0 commit comments