@@ -1235,6 +1235,118 @@ func TestWriterFlushAtCloseEmulated(t *testing.T) {
12351235 })
12361236}
12371237
1238+ // Tests small flush (under 512 bytes) to verify that logic avoiding
1239+ // content type sniffing works as expected in this case.
1240+ func TestWriterSmallFlushEmulated (t * testing.T ) {
1241+ transportClientTest (skipHTTP ("appends only supported via gRPC" ), t , func (t * testing.T , ctx context.Context , project , bucket string , client storageClient ) {
1242+ // Create test bucket.
1243+ _ , err := client .CreateBucket (ctx , project , bucket , & BucketAttrs {
1244+ Name : bucket ,
1245+ }, nil )
1246+ if err != nil {
1247+ t .Fatalf ("client.CreateBucket: %v" , err )
1248+ }
1249+ prefix := time .Now ().Nanosecond ()
1250+ testCases := []struct {
1251+ initialBytes []byte
1252+ wantContentType string
1253+ }{
1254+ {
1255+ initialBytes : []byte {0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 },
1256+ wantContentType : "application/octet-stream" ,
1257+ },
1258+ {
1259+ initialBytes : []byte ("helloworld" ),
1260+ wantContentType : "text/plain; charset=utf-8" ,
1261+ },
1262+ }
1263+
1264+ for _ , tc := range testCases {
1265+ t .Run (tc .wantContentType , func (t * testing.T ) {
1266+ objName := fmt .Sprintf ("%d-object-%d" , prefix , time .Now ().Nanosecond ())
1267+
1268+ vc := & Client {tc : client }
1269+ w := vc .Bucket (bucket ).Object (objName ).NewWriter (ctx )
1270+ w .Append = true
1271+ w .ChunkSize = MiB
1272+ var gotOffsets []int64
1273+ w .ProgressFunc = func (offset int64 ) {
1274+ gotOffsets = append (gotOffsets , offset )
1275+ }
1276+ wantOffsets := []int64 {10 , 1010 , 1010 + MiB , 1010 + 2 * MiB , 3 * MiB }
1277+
1278+ // Make content with fixed first 10 bytes which will yield
1279+ // expected type when sniffed.
1280+ content := bytes .Clone (randomBytes3MiB )
1281+ copy (content , tc .initialBytes )
1282+
1283+ // Test Flush at a 10 byte offset.
1284+ n , err := w .Write (content [:10 ])
1285+ if err != nil {
1286+ t .Fatalf ("writing data: got %v; want ok" , err )
1287+ }
1288+ if n != 10 {
1289+ t .Errorf ("writing data: got %v bytes written, want %v" , n , 10 )
1290+ }
1291+ off , err := w .Flush ()
1292+ if err != nil {
1293+ t .Fatalf ("flush: got %v; want ok" , err )
1294+ }
1295+ if off != 10 {
1296+ t .Errorf ("flushing data: got %v bytes written, want %v" , off , 10 )
1297+ }
1298+ // Write another 1000 bytes and flush again.
1299+ n , err = w .Write (content [10 :1010 ])
1300+ if err != nil {
1301+ t .Fatalf ("writing data: got %v; want ok" , err )
1302+ }
1303+ if n != 1000 {
1304+ t .Errorf ("writing data: got %v bytes written, want %v" , n , 1000 )
1305+ }
1306+ off , err = w .Flush ()
1307+ if err != nil {
1308+ t .Fatalf ("flush: got %v; want ok" , err )
1309+ }
1310+ if off != 1010 {
1311+ t .Errorf ("flushing data: got %v bytes written, want %v" , off , 1010 )
1312+ }
1313+ // Write the rest of the object
1314+ _ , err = w .Write (content [1010 :])
1315+ if err != nil {
1316+ t .Fatalf ("writing data: got %v; want ok" , err )
1317+ }
1318+ if err := w .Close (); err != nil {
1319+ t .Fatalf ("closing writer: %v" , err )
1320+ }
1321+ // Check offsets
1322+ if ! slices .Equal (gotOffsets , wantOffsets ) {
1323+ t .Errorf ("progress offsets: got %v, want %v" , gotOffsets , wantOffsets )
1324+ }
1325+
1326+ // Download object and check data
1327+ r , err := veneerClient .Bucket (bucket ).Object (objName ).NewReader (ctx )
1328+ defer r .Close ()
1329+ if err != nil {
1330+ t .Fatalf ("opening reading: %v" , err )
1331+ }
1332+ wantLen := 3 * MiB
1333+ got , err := io .ReadAll (r )
1334+ if n := len (got ); n != wantLen {
1335+ t .Fatalf ("expected to read %d bytes, but got %d (%v)" , wantLen , n , err )
1336+ }
1337+ if diff := cmp .Diff (got , content ); diff != "" {
1338+ t .Errorf ("checking written content: got(-), want(+):\n %s" , diff )
1339+ }
1340+ // Check expected content type.
1341+ if got , want := r .Attrs .ContentType , tc .wantContentType ; got != want {
1342+ t .Errorf ("content type: got %v, want %v" , got , want )
1343+ }
1344+ })
1345+ }
1346+
1347+ })
1348+ }
1349+
12381350func TestListNotificationsEmulated (t * testing.T ) {
12391351 transportClientTest (skipGRPC ("notifications not implemented" ), t , func (t * testing.T , ctx context.Context , project , bucket string , client storageClient ) {
12401352 // Populate test object.
0 commit comments