2424import static org .junit .Assume .assumeTrue ;
2525
2626import com .google .api .core .ApiFunction ;
27+ import com .google .api .gax .core .GaxProperties ;
2728import com .google .api .gax .rpc .ApiCallContext ;
29+ import com .google .api .gax .rpc .HeaderProvider ;
2830import com .google .auth .oauth2 .AccessToken ;
2931import com .google .auth .oauth2 .OAuth2Credentials ;
3032import com .google .cloud .spanner .DatabaseAdminClient ;
@@ -151,6 +153,8 @@ public class GapicSpannerRpcTest {
151153 private Server server ;
152154 private InetSocketAddress address ;
153155 private final Map <SpannerRpc .Option , Object > optionsMap = new HashMap <>();
156+ private Metadata seenHeaders ;
157+ private String defaultUserAgent ;
154158
155159 @ BeforeClass
156160 public static void checkNotEmulator () {
@@ -161,6 +165,7 @@ public static void checkNotEmulator() {
161165
162166 @ Before
163167 public void startServer () throws IOException {
168+ defaultUserAgent = "spanner-java/" + GaxProperties .getLibraryVersion (GapicSpannerRpc .class );
164169 mockSpanner = new MockSpannerServiceImpl ();
165170 mockSpanner .setAbortProbability (0.0D ); // We don't want any unpredictable aborted transactions.
166171 mockSpanner .putStatementResult (StatementResult .query (SELECT1AND2 , SELECT1_RESULTSET ));
@@ -183,6 +188,7 @@ public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
183188 ServerCall <ReqT , RespT > call ,
184189 Metadata headers ,
185190 ServerCallHandler <ReqT , RespT > next ) {
191+ seenHeaders = headers ;
186192 String auth =
187193 headers .get (Key .of ("authorization" , Metadata .ASCII_STRING_MARSHALLER ));
188194 assertThat (auth ).isEqualTo ("Bearer " + VARIABLE_OAUTH_TOKEN );
@@ -502,6 +508,50 @@ public void testAdminRequestsLimitExceededRetryAlgorithm() {
502508 assertThat (alg .shouldRetry (new Exception ("random exception" ), null )).isFalse ();
503509 }
504510
511+ @ Test
512+ public void testDefaultUserAgent () {
513+ final SpannerOptions options = createSpannerOptions ();
514+ try (final Spanner spanner = options .getService ()) {
515+ final DatabaseClient databaseClient =
516+ spanner .getDatabaseClient (DatabaseId .of ("[PROJECT]" , "[INSTANCE]" , "[DATABASE]" ));
517+
518+ try (final ResultSet rs = databaseClient .singleUse ().executeQuery (SELECT1AND2 )) {
519+ rs .next ();
520+ }
521+
522+ assertThat (seenHeaders .get (Key .of ("user-agent" , Metadata .ASCII_STRING_MARSHALLER )))
523+ .contains (defaultUserAgent );
524+ }
525+ }
526+
527+ @ Test
528+ public void testCustomUserAgent () {
529+ for (final String headerId : new String [] {"user-agent" , "User-Agent" , "USER-AGENT" }) {
530+ final HeaderProvider userAgentHeaderProvider =
531+ new HeaderProvider () {
532+ @ Override
533+ public Map <String , String > getHeaders () {
534+ final Map <String , String > headers = new HashMap <>();
535+ headers .put (headerId , "test-agent" );
536+ return headers ;
537+ }
538+ };
539+ final SpannerOptions options =
540+ createSpannerOptions ().toBuilder ().setHeaderProvider (userAgentHeaderProvider ).build ();
541+ try (Spanner spanner = options .getService ()) {
542+ final DatabaseClient databaseClient =
543+ spanner .getDatabaseClient (DatabaseId .of ("[PROJECT]" , "[INSTANCE]" , "[DATABASE]" ));
544+
545+ try (final ResultSet rs = databaseClient .singleUse ().executeQuery (SELECT1AND2 )) {
546+ rs .next ();
547+ }
548+
549+ assertThat (seenHeaders .get (Key .of ("user-agent" , Metadata .ASCII_STRING_MARSHALLER )))
550+ .contains ("test-agent " + defaultUserAgent );
551+ }
552+ }
553+ }
554+
505555 @ SuppressWarnings ("rawtypes" )
506556 private SpannerOptions createSpannerOptions () {
507557 String endpoint = address .getHostString () + ":" + server .getPort ();
0 commit comments