@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
22import { readFileSync } from "node:fs" ;
33import { fileURLToPath } from "node:url" ;
44import { dirname , resolve } from "node:path" ;
5+ import { spawnSync } from "node:child_process" ;
56
67import worker , { __test } from "../web/strategy-switch-console/worker.js" ;
78import { DEFAULT_ACCOUNT_OPTIONS , RUNTIME_CATALOG_PROJECTION } from "../web/strategy-switch-console/config.js" ;
@@ -28,6 +29,28 @@ assert.deepEqual(
2829 [ "schema_version" , "source_ledger_sha256" , "source_generated_at" , "source_computed_at" , "viewed_at" , "data_status" , "summary" , "subjects" , "policy" , "errors" ] ,
2930) ;
3031assert . equal ( m0ResearchPublisherEnvelopeSchema [ "x-qsl-canonical-utf8-max-bytes" ] , 256 * 1024 ) ;
32+
33+ function buildQrsCanonicalM0PublisherBody ( sourceSnapshotPath ) {
34+ // The fixture is a minimized, unchanged QAR #66 emitted source snapshot.
35+ // Execute QRS #309/#310's real offline builder and return its exact sorted-
36+ // key canonical UTF-8 request body, rather than duplicating it in JS.
37+ const publisherCode = [
38+ "import hashlib,json,sys" ,
39+ "sys.path.insert(0, sys.argv[2])" ,
40+ "from build_m0_research_publisher_envelope import build_m0_research_publisher_envelope, canonical_envelope_body" ,
41+ "raw=open(sys.argv[1], 'rb').read()" ,
42+ "source=json.loads(raw)" ,
43+ "envelope=build_m0_research_publisher_envelope(source_snapshot=source, source_artifact={'repository':'QuantStrategyLab/QuantAdvisorResearch','revision':'9e06f248fb60d1c995426e66468cb18454612e9b','run_id':'qar66-fixture-run','artifact_id':'QAR66:source/snapshot','sha256':hashlib.sha256(raw).hexdigest()}, producer_repository='QuantStrategyLab/QuantRuntimeSettings', producer_revision='451b11d0bf6ba2632ca2227c850e7236a40d12e5', now=source['generated_at'])" ,
44+ "sys.stdout.buffer.write(canonical_envelope_body(envelope))" ,
45+ ] . join ( "; " ) ;
46+ const result = spawnSync (
47+ "python3" ,
48+ [ "-c" , publisherCode , sourceSnapshotPath , resolve ( root , "python/scripts" ) ] ,
49+ { cwd : root , encoding : "buffer" } ,
50+ ) ;
51+ assert . equal ( result . status , 0 , Buffer . from ( result . stderr || "" ) . toString ( "utf8" ) ) ;
52+ return Buffer . from ( result . stdout ) ;
53+ }
3154assert . ok ( __test . currentStrategiesTimeoutMs >= 8000 ) ;
3255const renderPlatformsBody = indexHtml . match ( / f u n c t i o n r e n d e r P l a t f o r m s \( \) \{ ( [ \s \S ] * ?) \n \} / ) ?. [ 1 ] || "" ;
3356assert . ok ( ! renderPlatformsBody . includes ( "syncStrategyForAccount(" ) ) ;
@@ -2815,6 +2838,25 @@ assert.match(m0ResearchPayload.viewed_at, /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z
28152838assert . equal ( m0ResearchPayload . data_status , "ready" ) ;
28162839assert . equal ( m0ResearchPayload . policy . no_order , true ) ;
28172840assert . equal ( m0ResearchPayload . subjects [ 0 ] . subject . identifier , "semiconductors" ) ;
2841+ const qar66SourceSnapshotPath = resolve ( root , "tests/fixtures/qar66-m0-research-source-snapshot.json" ) ;
2842+ const qrsCanonicalM0PublisherBody = buildQrsCanonicalM0PublisherBody ( qar66SourceSnapshotPath ) ;
2843+ assert . ok ( qrsCanonicalM0PublisherBody . byteLength <= m0ResearchPublisherEnvelopeSchema [ "x-qsl-canonical-utf8-max-bytes" ] ) ;
2844+ const qrsCanonicalM0Envelope = JSON . parse ( qrsCanonicalM0PublisherBody . toString ( "utf8" ) ) ;
2845+ assert . deepEqual (
2846+ Object . keys ( qrsCanonicalM0Envelope . ledger . summary ) ,
2847+ [ ...Object . keys ( qrsCanonicalM0Envelope . ledger . summary ) ] . sort ( ) ,
2848+ ) ;
2849+ await __test . normalizeM0ResearchLedgerTransport ( qrsCanonicalM0Envelope ) ;
2850+ const qrsCanonicalM0Post = await worker . fetch (
2851+ new Request ( "https://switch.example/api/internal/sync-m0-research-ledger" , {
2852+ method : "POST" ,
2853+ headers : { Authorization : `Bearer ${ m0ResearchSyncValue } ` , "Content-Type" : "application/json" } ,
2854+ body : qrsCanonicalM0PublisherBody ,
2855+ } ) ,
2856+ m0ResearchEnv ,
2857+ ) ;
2858+ assert . equal ( qrsCanonicalM0Post . status , 200 ) ;
2859+ assert . equal ( ( await qrsCanonicalM0Post . json ( ) ) . no_order , true ) ;
28182860const m0ExpiredReadProjection = __test . projectM0ResearchDashboardForRead (
28192861 m0ResearchLedger ,
28202862 m0ResearchLedgerSha ,
0 commit comments