@@ -14,6 +14,11 @@ use chrono::offset::Utc;
1414use hawk:: { self , Credentials , Key , RequestBuilder } ;
1515use hmac:: { Hmac , KeyInit , Mac } ;
1616use http:: StatusCode ;
17+ use httptest:: {
18+ Expectation , Server ,
19+ matchers:: { all_of, contains, matches, request, url_decoded} ,
20+ responders:: status_code,
21+ } ;
1722use lazy_static:: lazy_static;
1823use serde:: de:: DeserializeOwned ;
1924use serde_json:: { Value , json} ;
@@ -113,6 +118,11 @@ async fn get_test_state(settings: &Settings) -> ServerState {
113118 deadman : Arc :: new ( RwLock :: new ( Deadman :: from ( & settings. syncstorage ) ) ) ,
114119 glean_logger,
115120 glean_enabled : settings. syncstorage . glean_enabled ,
121+ gcs_payload_bucket : settings. syncstorage . gcs_payload_bucket . clone ( ) ,
122+ gcs_payload_offload_collections : Arc :: new (
123+ settings. syncstorage . gcs_payload_offload_collections . clone ( ) ,
124+ ) ,
125+ gcs_endpoint : settings. syncstorage . gcs_endpoint . clone ( ) ,
116126 }
117127}
118128
@@ -446,6 +456,7 @@ async fn post_collection() {
446456 id: "foo" . to_string( ) ,
447457 sortindex: Some ( 0 ) ,
448458 payload: Some ( "bar" . to_string( ) ) ,
459+ payload_link: None ,
449460 ttl: Some ( 31_536_000 ) ,
450461 } ] ) ;
451462 let bytes =
@@ -892,3 +903,79 @@ async fn error_endpoint_logging_check() {
892903 )
893904 . await ;
894905}
906+
907+ #[ actix_rt:: test]
908+ async fn put_bso_offloads_to_gcs ( ) {
909+ let mut settings = get_test_settings ( ) ;
910+ if !settings. syncstorage . uses_spanner ( ) {
911+ // TODO: should be Spanner only (for now)
912+ return ;
913+ }
914+
915+ // Mock GCS: match the JSON multipart upload to bsos's bucket. The matcher
916+ // inspects the multipart body for `committed=false` metadata and a
917+ // `customTime` field; if either is missing the expectation goes
918+ // unsatisfied and Server::Drop panics.
919+ let server = Server :: run ( ) ;
920+ server. expect (
921+ Expectation :: matching ( all_of ! [
922+ request:: method_path( "POST" , "/upload/storage/v1/b/test-bucket/o" ) ,
923+ request:: query( url_decoded( contains( ( "uploadType" , "multipart" ) ) ) ) ,
924+ request:: body( matches( r#""committed""# ) ) ,
925+ request:: body( matches( r#""false""# ) ) ,
926+ request:: body( matches( r#""customTime""# ) ) ,
927+ ] )
928+ . respond_with (
929+ status_code ( 200 )
930+ . append_header ( "content-type" , "application/json" )
931+ . body (
932+ serde_json:: json!( {
933+ "name" : "test-object" ,
934+ "bucket" : "test-bucket" ,
935+ } )
936+ . to_string ( ) ,
937+ ) ,
938+ ) ,
939+ ) ;
940+
941+ settings. syncstorage . gcs_payload_bucket = Some ( "test-bucket" . to_owned ( ) ) ;
942+ settings. syncstorage . gcs_payload_offload_collections = vec ! [ "bookmarks" . to_owned( ) ] ;
943+ settings. syncstorage . gcs_endpoint = Some ( format ! ( "http://{}" , server. addr( ) ) ) ;
944+
945+ let app = init_app ! ( settings) . await ;
946+
947+ let req = create_request (
948+ http:: Method :: PUT ,
949+ "/1.5/42/storage/bookmarks/wibble" ,
950+ None ,
951+ Some ( json ! ( { "payload" : "hello world" } ) ) ,
952+ )
953+ . to_request ( ) ;
954+ let resp = app
955+ . call ( req)
956+ . await
957+ . expect ( "Could not get resp in put_bso_offloads_to_gcs" ) ;
958+ assert ! (
959+ resp. response( ) . status( ) . is_success( ) ,
960+ "put_bso failed: {:?}" ,
961+ resp. response( )
962+ ) ;
963+
964+ // mock shouldn't see tabs offloaded to GCS
965+ let req = create_request (
966+ http:: Method :: PUT ,
967+ "/1.5/42/storage/tabs/wobble" ,
968+ None ,
969+ Some ( json ! ( { "payload" : "hello world" } ) ) ,
970+ )
971+ . to_request ( ) ;
972+ let resp = app
973+ . call ( req)
974+ . await
975+ . expect ( "Could not get resp in put_bso_offloads_to_gcs" ) ;
976+ assert ! (
977+ resp. response( ) . status( ) . is_success( ) ,
978+ "put_bso failed: {:?}" ,
979+ resp. response( )
980+ ) ;
981+ }
0 commit comments