@@ -22,6 +22,11 @@ use std::time::Duration;
2222
2323use base64:: engine:: general_purpose:: STANDARD as BASE64 ;
2424use base64:: Engine ;
25+ use buzz_core:: job:: {
26+ JobDisposition , JobResultPayload , JobVerification , JobVerificationStatus ,
27+ JOB_RESULT_SCHEMA_VERSION ,
28+ } ;
29+ use buzz_core:: kind:: { KIND_JOB_REQUEST , KIND_JOB_RESULT } ;
2530use buzz_test_client:: { BuzzTestClient , RelayMessage , TestClientError } ;
2631use nostr:: { Alphabet , EventBuilder , Filter , Keys , Kind , SingleLetterTag , Tag } ;
2732use sha2:: { Digest , Sha256 } ;
@@ -414,6 +419,101 @@ async fn test_send_event_and_receive_via_subscription() {
414419 client_b. disconnect ( ) . await . expect ( "disconnect B" ) ;
415420}
416421
422+ #[ tokio:: test]
423+ #[ ignore]
424+ async fn test_structured_and_legacy_job_results_round_trip ( ) {
425+ let url = relay_url ( ) ;
426+ let keys = Keys :: generate ( ) ;
427+ let channel = create_test_channel ( & keys) . await ;
428+ let channel_id = Uuid :: parse_str ( & channel) . expect ( "channel UUID" ) ;
429+ let mut client = BuzzTestClient :: connect ( & url, & keys) . await . expect ( "connect" ) ;
430+
431+ let request = EventBuilder :: new (
432+ Kind :: Custom ( KIND_JOB_REQUEST as u16 ) ,
433+ "Prepare the release handoff" ,
434+ )
435+ . tags ( [ Tag :: parse ( [ "h" , channel. as_str ( ) ] ) . expect ( "request h tag" ) ] )
436+ . sign_with_keys ( & keys)
437+ . expect ( "sign job request" ) ;
438+ let ok = client
439+ . send_event ( request. clone ( ) )
440+ . await
441+ . expect ( "send job request" ) ;
442+ assert ! ( ok. accepted, "relay rejected job request: {}" , ok. message) ;
443+
444+ let payload = JobResultPayload {
445+ schema_version : JOB_RESULT_SCHEMA_VERSION ,
446+ job_request : request. id . to_hex ( ) ,
447+ requested_outcome : "Prepare the release handoff" . into ( ) ,
448+ outcome : "Release handoff is ready" . into ( ) ,
449+ last_progress : Some ( "Verified the release checklist" . into ( ) ) ,
450+ disposition : JobDisposition :: NoArtifact ,
451+ artifacts : Vec :: new ( ) ,
452+ verification : vec ! [ JobVerification {
453+ label: "Release checklist" . into( ) ,
454+ status: JobVerificationStatus :: Passed ,
455+ evidence: Some ( "All required items passed" . into( ) ) ,
456+ } ] ,
457+ blocker : None ,
458+ } ;
459+ let structured = buzz_sdk:: build_job_result ( channel_id, request. id , & payload)
460+ . expect ( "build structured result" )
461+ . sign_with_keys ( & keys)
462+ . expect ( "sign structured result" ) ;
463+ let ok = client
464+ . send_event ( structured. clone ( ) )
465+ . await
466+ . expect ( "send structured result" ) ;
467+ assert ! (
468+ ok. accepted,
469+ "relay rejected structured result: {}" ,
470+ ok. message
471+ ) ;
472+
473+ let legacy_content = "Release handoff completed. Checklist passed." ;
474+ let legacy = EventBuilder :: new ( Kind :: Custom ( KIND_JOB_RESULT as u16 ) , legacy_content)
475+ . tags ( [
476+ Tag :: parse ( [ "h" , channel. as_str ( ) ] ) . expect ( "legacy h tag" ) ,
477+ Tag :: parse ( [ "e" , & request. id . to_hex ( ) , "" , "reply" ] ) . expect ( "legacy reply tag" ) ,
478+ ] )
479+ . sign_with_keys ( & keys)
480+ . expect ( "sign legacy result" ) ;
481+ let ok = client
482+ . send_event ( legacy. clone ( ) )
483+ . await
484+ . expect ( "send legacy result" ) ;
485+ assert ! ( ok. accepted, "relay rejected legacy result: {}" , ok. message) ;
486+
487+ let sid = sub_id ( "job-result-round-trip" ) ;
488+ let filter = Filter :: new ( )
489+ . kind ( Kind :: Custom ( KIND_JOB_RESULT as u16 ) )
490+ . custom_tags ( SingleLetterTag :: lowercase ( Alphabet :: H ) , [ channel. as_str ( ) ] ) ;
491+ client
492+ . subscribe ( & sid, vec ! [ filter] )
493+ . await
494+ . expect ( "subscribe to job results" ) ;
495+ let events = client
496+ . collect_until_eose ( & sid, Duration :: from_secs ( 5 ) )
497+ . await
498+ . expect ( "query job results" ) ;
499+
500+ let stored_structured = events
501+ . iter ( )
502+ . find ( |event| event. id == structured. id )
503+ . expect ( "structured result returned by relay" ) ;
504+ assert_eq ! (
505+ stored_structured. content,
506+ payload. to_json( ) . expect( "payload JSON" )
507+ ) ;
508+ let stored_legacy = events
509+ . iter ( )
510+ . find ( |event| event. id == legacy. id )
511+ . expect ( "legacy result returned by relay" ) ;
512+ assert_eq ! ( stored_legacy. content, legacy_content) ;
513+
514+ client. disconnect ( ) . await . expect ( "disconnect" ) ;
515+ }
516+
417517#[ tokio:: test]
418518#[ ignore]
419519async fn test_large_event_frame_below_configured_limit_is_accepted ( ) {
0 commit comments