@@ -301,6 +301,47 @@ func TestBuildActiveRegistrationDocument_KeepsOperatorDescription(t *testing.T)
301301 }
302302}
303303
304+ func TestBuildActiveRegistrationDocument_PublishesAgentOfferMetadata (t * testing.T ) {
305+ owner := & monetizeapi.ServiceOffer {
306+ ObjectMeta : metav1.ObjectMeta {Name : "demo-quant" , Namespace : "agent-demo-quant" },
307+ Spec : monetizeapi.ServiceOfferSpec {
308+ Type : "agent" ,
309+ Path : "/services/demo-quant" ,
310+ Registration : monetizeapi.ServiceOfferRegistration {
311+ Enabled : true ,
312+ Name : "demo-quant" ,
313+ Description : "Agent-backed chain analyst" ,
314+ Skills : []string {"ethereum-networks" , "addresses" },
315+ Metadata : map [string ]string {
316+ "runtime" : "hermes" ,
317+ "model" : "qwen3.5:9b" ,
318+ "pricingUnit" : "agent-turn" ,
319+ "x402Price" : "10" ,
320+ "x402Asset" : "OBOL" ,
321+ "x402Network" : "ethereum" ,
322+ },
323+ },
324+ },
325+ }
326+
327+ doc := buildActiveRegistrationDocument (owner , []* monetizeapi.ServiceOffer {owner }, "https://seller.example" , "42" )
328+ for k , want := range map [string ]string {
329+ "runtime" : "hermes" ,
330+ "model" : "qwen3.5:9b" ,
331+ "pricingUnit" : "agent-turn" ,
332+ "x402Price" : "10" ,
333+ "x402Asset" : "OBOL" ,
334+ "x402Network" : "ethereum" ,
335+ } {
336+ if got := doc .Metadata [k ]; got != want {
337+ t .Errorf ("metadata[%s] = %q, want %q (full=%v)" , k , got , want , doc .Metadata )
338+ }
339+ }
340+ if len (doc .Registrations ) != 1 || doc .Registrations [0 ].AgentID != 42 {
341+ t .Errorf ("registrations = %+v, want agentId 42" , doc .Registrations )
342+ }
343+ }
344+
304345// TestBuildActiveRegistrationDocument_FallsBackToModelDescriptionForInference
305346// pins the *other* side of the description contract: when the operator does
306347// not supply a description, inference offers should still get the
@@ -621,6 +662,62 @@ func TestBuildServiceCatalogJSON_Empty(t *testing.T) {
621662 }
622663}
623664
665+ func TestBuildServiceCatalogJSON_AgentOfferUsesResolvedModel (t * testing.T ) {
666+ offer := & monetizeapi.ServiceOffer {
667+ ObjectMeta : metav1.ObjectMeta {Name : "demo-quant" , Namespace : "agent-demo-quant" },
668+ Spec : monetizeapi.ServiceOfferSpec {
669+ Type : "agent" ,
670+ Payment : monetizeapi.ServiceOfferPayment {
671+ Network : "ethereum" ,
672+ PayTo : "0x1111111111111111111111111111111111111111" ,
673+ Asset : monetizeapi.ServiceOfferAsset {
674+ Address : "0x2222222222222222222222222222222222222222" ,
675+ Symbol : "OBOL" ,
676+ Decimals : 18 ,
677+ TransferMethod : "permit2" ,
678+ EIP712Name : "OBOL" ,
679+ EIP712Version : "1" ,
680+ },
681+ Price : monetizeapi.ServiceOfferPriceTable {PerRequest : "10" },
682+ },
683+ Registration : monetizeapi.ServiceOfferRegistration {
684+ Description : "Agent-backed chain analyst" ,
685+ },
686+ },
687+ Status : monetizeapi.ServiceOfferStatus {
688+ AgentResolution : & monetizeapi.ServiceOfferAgentResolution {
689+ Model : "qwen3.5:9b" ,
690+ Runtime : "hermes" ,
691+ },
692+ Conditions : []monetizeapi.Condition {{Type : "Ready" , Status : "True" }},
693+ },
694+ }
695+
696+ jsonStr := buildServiceCatalogJSON ([]* monetizeapi.ServiceOffer {offer }, "https://seller.example" )
697+ assertServiceCatalogSchema (t , jsonStr )
698+
699+ var services []schemas.ServiceCatalogEntry
700+ if err := json .Unmarshal ([]byte (jsonStr ), & services ); err != nil {
701+ t .Fatalf ("invalid JSON: %v\n %s" , err , jsonStr )
702+ }
703+ if len (services ) != 1 {
704+ t .Fatalf ("expected 1 service, got %d: %s" , len (services ), jsonStr )
705+ }
706+ svc := services [0 ]
707+ if svc .Type != "agent" {
708+ t .Errorf ("type = %q, want agent" , svc .Type )
709+ }
710+ if svc .Model != "qwen3.5:9b" {
711+ t .Errorf ("model = %q, want qwen3.5:9b" , svc .Model )
712+ }
713+ if svc .Price != "10 OBOL/request" {
714+ t .Errorf ("price = %q, want 10 OBOL/request" , svc .Price )
715+ }
716+ if svc .Endpoint != "https://seller.example/services/demo-quant" {
717+ t .Errorf ("endpoint = %q" , svc .Endpoint )
718+ }
719+ }
720+
624721// TestBuildServiceCatalogJSON_ExcludesNonReady locks in the filter pipeline:
625722// nil offers, paused offers, and offers with a DeletionTimestamp must never
626723// leak onto the public storefront, even if they carry Ready=True.
0 commit comments