@@ -146,3 +146,65 @@ func TestUnitAccountInfoQueryMock(t *testing.T) {
146146 require .NoError (t , err )
147147 require .Equal (t , HbarFromTinybar (2 ), cost )
148148}
149+
150+ // A paid query on a client without an operator must fail cleanly instead of panicking while
151+ // building the payment.
152+ func TestUnitAccountInfoQueryExecuteWithoutOperator (t * testing.T ) {
153+ t .Parallel ()
154+
155+ client , server := NewMockClientAndServer ([][]interface {}{{}})
156+ defer server .Close ()
157+
158+ client .operator = nil
159+
160+ // An explicit payment skips the cost lookup and goes straight to payment signing.
161+ _ , err := NewAccountInfoQuery ().
162+ SetAccountID (AccountID {Account : 1800 }).
163+ SetNodeAccountIDs ([]AccountID {{Account : 3 }}).
164+ SetQueryPayment (NewHbar (1 )).
165+ Execute (client )
166+ require .ErrorIs (t , err , errNoClientProvided )
167+ }
168+
169+ // A COST_ANSWER query is unpaid, so it must go out without a payment even on a query object a
170+ // previous Execute already attached the client to - reaching for the absent operator there would
171+ // panic while building the payment.
172+ func TestUnitAccountInfoQueryGetCostAfterExecuteWithoutOperator (t * testing.T ) {
173+ t .Parallel ()
174+
175+ var captured []* services.Query
176+ client , server := NewMockClientAndServer ([][]interface {}{{_CostAnswer (services .ResponseCodeEnum_OK , & captured )}})
177+ defer server .Close ()
178+
179+ client .operator = nil
180+
181+ query := NewAccountInfoQuery ().
182+ SetAccountID (AccountID {Account : 1800 }).
183+ SetNodeAccountIDs ([]AccountID {{Account : 3 }})
184+
185+ // Fails for want of an operator, but leaves the client on the query.
186+ _ , err := query .Execute (client )
187+ require .ErrorIs (t , err , errNoClientProvided )
188+ require .NotNil (t , query .client )
189+
190+ cost , err := query .GetCost (client )
191+ require .NoError (t , err )
192+ require .Equal (t , HbarFromTinybar (25 ), cost )
193+
194+ // The failed Execute must not have reached the network, so this is the cost lookup.
195+ require .Len (t , captured , 1 )
196+ info , ok := captured [0 ].Query .(* services.Query_CryptoGetInfo )
197+ require .True (t , ok )
198+ require .Equal (t , services .ResponseType_COST_ANSWER , info .CryptoGetInfo .Header .ResponseType )
199+ require .Nil (t , info .CryptoGetInfo .Header .Payment , "a COST_ANSWER query must not attach a payment transaction" )
200+ }
201+
202+ // GetCost must fail cleanly without a client.
203+ func TestUnitAccountInfoQueryGetCostNilClient (t * testing.T ) {
204+ t .Parallel ()
205+
206+ _ , err := NewAccountInfoQuery ().
207+ SetAccountID (AccountID {Account : 1800 }).
208+ GetCost (nil )
209+ require .ErrorIs (t , err , errNoClientProvided )
210+ }
0 commit comments