Repository: fsouza/fake-gcs-server
Pull request #1523 (2024-03-08 18:28:23)

build(deps): bump google.golang.org/api from 0.168.0 to 0.169.0 in /examples/go
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.168.0 to 0.169.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/googleapis/google-api-go-client/releases">google.golang.org/api's releases</a>.</em></p>
<blockquote>
<h2>v0.169.0</h2>
<h2><a href="https://github.com/googleapis/google-api-go-client/compare/v0.168.0...v0.169.0">0.169.0</a> (2024-03-07)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2450">#2450</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/d22da1806db891da12d7290ef4334e18fbaf0ca2">d22da18</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2454">#2454</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/2675c0abf9e442bb3ac35e18ecc196aaaa4facd3">2675c0a</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2457">#2457</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/a488112cd111b883dfaffa7e4ab67e99a6ea9b90">a488112</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md">google.golang.org/api's changelog</a>.</em></p>
<blockquote>
<h2><a href="https://github.com/googleapis/google-api-go-client/compare/v0.168.0...v0.169.0">0.169.0</a> (2024-03-07)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2450">#2450</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/d22da1806db891da12d7290ef4334e18fbaf0ca2">d22da18</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2454">#2454</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/2675c0abf9e442bb3ac35e18ecc196aaaa4facd3">2675c0a</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2457">#2457</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/a488112cd111b883dfaffa7e4ab67e99a6ea9b90">a488112</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/googleapis/google-api-go-client/commit/9c2cde866c0c5f37b2fc7428b76c3bd0f7be3b5f"><code>9c2cde8</code></a> chore(main): release 0.169.0 (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2451">#2451</a>)</li>
<li><a href="https://github.com/googleapis/google-api-go-client/commit/a488112cd111b883dfaffa7e4ab67e99a6ea9b90"><code>a488112</code></a> feat(all): auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2457">#2457</a>)</li>
<li><a href="https://github.com/googleapis/google-api-go-client/commit/2675c0abf9e442bb3ac35e18ecc196aaaa4facd3"><code>2675c0a</code></a> feat(all): auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2454">#2454</a>)</li>
<li><a href="https://github.com/googleapis/google-api-go-client/commit/d22da1806db891da12d7290ef4334e18fbaf0ca2"><code>d22da18</code></a> feat(all): auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2450">#2450</a>)</li>
<li>See full diff in <a href="https://github.com/googleapis/google-api-go-client/compare/v0.168.0...v0.169.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/api&package-manager=go_modules&previous-version=0.168.0&new-version=0.169.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/.github/workflows/docker-push.yml b/.github/workflows/docker-push.yml
index e69aa2f..a5fee7f 100644
--- a/.github/workflows/docker-push.yml
+++ b/.github/workflows/docker-push.yml
@@ -38,7 +38,7 @@ jobs:
           password: ${{ secrets.DOCKER_PASSWORD }}
 
       - name: build and push
-        uses: docker/build-push-action@v5.1.0
+        uses: docker/build-push-action@v5.2.0
         with:
           cache-from: fsouzaci/gha-cache:fake-gcs-server
           cache-to: fsouzaci/gha-cache:fake-gcs-server
diff --git a/examples/go/go.mod b/examples/go/go.mod
index 911ef42..1b7f7f1 100644
--- a/examples/go/go.mod
+++ b/examples/go/go.mod
@@ -4,7 +4,7 @@ go 1.22
 
 require (
 	cloud.google.com/go/storage v1.39.1
-	google.golang.org/api v0.168.0
+	google.golang.org/api v0.169.0
 )
 
 require (
diff --git a/examples/go/go.sum b/examples/go/go.sum
index 61d3393..3c0b4a7 100644
--- a/examples/go/go.sum
+++ b/examples/go/go.sum
@@ -152,8 +152,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
-google.golang.org/api v0.168.0 h1:MBRe+Ki4mMN93jhDDbpuRLjRddooArz4FeSObvUMmjY=
-google.golang.org/api v0.168.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg=
+google.golang.org/api v0.169.0 h1:QwWPy71FgMWqJN/l6jVlFHUa29a7dcUy02I8o799nPY=
+google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
diff --git a/examples/node/package-lock.json b/examples/node/package-lock.json
index a25c739..ea4687e 100644
--- a/examples/node/package-lock.json
+++ b/examples/node/package-lock.json
@@ -24,9 +24,9 @@
       "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g=="
     },
     "@google-cloud/storage": {
-      "version": "7.7.0",
-      "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.7.0.tgz",
-      "integrity": "sha512-EMCEY+6JiIkx7Dt8NXVGGjy1vRdSGdHkoqZoqjJw7cEBkT7ZkX0c7puedfn1MamnzW5SX4xoa2jVq5u7OWBmkQ==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.8.0.tgz",
+      "integrity": "sha512-4q8rKdLp35z8msAtrhr0pbos7BeD8T0tr6rMbBINewp9cfrwj7ROIElVwBluU8fZ596OvwQcjb6QCyBzTmkMRQ==",
       "requires": {
         "@google-cloud/paginator": "^5.0.0",
         "@google-cloud/projectify": "^4.0.0",
@@ -34,11 +34,11 @@
         "abort-controller": "^3.0.0",
         "async-retry": "^1.3.3",
         "compressible": "^2.0.12",
-        "duplexify": "^4.0.0",
+        "duplexify": "^4.1.3",
         "ent": "^2.2.0",
         "fast-xml-parser": "^4.3.0",
         "gaxios": "^6.0.2",
-        "google-auth-library": "^9.0.0",
+        "google-auth-library": "^9.6.3",
         "mime": "^3.0.0",
         "mime-types": "^2.0.8",
         "p-limit": "^3.0.1",
@@ -58,9 +58,9 @@
       "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg=="
     },
     "@types/node": {
-      "version": "20.10.1",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.1.tgz",
-      "integrity": "sha512-T2qwhjWwGH81vUEx4EXmBKsTJRXFXNZTL4v0gi01+zyBmCwzE6TyHszqX01m+QHTEq+EZNo13NeJIdEqf+Myrg==",
+      "version": "20.11.25",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.25.tgz",
+      "integrity": "sha512-TBHyJxk2b7HceLVGFcpAUjsa5zIdsPWlR6XHfyGzd0SFu+/NFgQgMAl96MSDZgQDvJAvV6BKsFOrt6zIL09JDw==",
       "requires": {
         "undici-types": "~5.26.4"
       }
@@ -160,14 +160,14 @@
       "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
     },
     "duplexify": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz",
-      "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==",
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
+      "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
       "requires": {
         "end-of-stream": "^1.4.1",
         "inherits": "^2.0.3",
         "readable-stream": "^3.1.1",
-        "stream-shift": "^1.0.0"
+        "stream-shift": "^1.0.2"
       }
     },
     "ecdsa-sig-formatter": {
@@ -202,9 +202,9 @@
       "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
     },
     "fast-xml-parser": {
-      "version": "4.3.2",
-      "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.2.tgz",
-      "integrity": "sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg==",
+      "version": "4.3.5",
+      "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.5.tgz",
+      "integrity": "sha512-sWvP1Pl8H03B8oFJpFR3HE31HUfwtX7Rlf9BNsvdpujD4n7WMhfmu8h9wOV2u+c1k0ZilTADhPqypzx2J690ZQ==",
       "requires": {
         "strnum": "^1.0.5"
       }
@@ -220,9 +220,9 @@
       }
     },
     "gaxios": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.1.1.tgz",
-      "integrity": "sha512-bw8smrX+XlAoo9o1JAksBwX+hi/RG15J+NTSxmNPIclKC3ZVK6C2afwY8OSdRvOK0+ZLecUJYtj2MmjOt3Dm0w==",
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.3.0.tgz",
+      "integrity": "sha512-p+ggrQw3fBwH2F5N/PAI4k/G/y1art5OxKpb2J2chwNNHM4hHuAOtivjPuirMF4KNKwTTUal/lPfL2+7h2mEcg==",
       "requires": {
         "extend": "^3.0.2",
         "https-proxy-agent": "^7.0.1",
@@ -240,22 +240,22 @@
       }
     },
     "google-auth-library": {
-      "version": "9.4.0",
-      "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.4.0.tgz",
-      "integrity": "sha512-Sn7w6p59lP3sHWPjLBc5OY6Y44CwbRrst5vAVpVhtSeMzTzZAGheyovaHDkcRRl6QeGl+H3a1GNiHLcLsCJTCw==",
+      "version": "9.6.3",
+      "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.6.3.tgz",
+      "integrity": "sha512-4CacM29MLC2eT9Cey5GDVK4Q8t+MMp8+OEdOaqD9MG6b0dOyLORaaeJMPQ7EESVgm/+z5EKYyFLxgzBJlJgyHQ==",
       "requires": {
         "base64-js": "^1.3.0",
         "ecdsa-sig-formatter": "^1.0.11",
-        "gaxios": "^6.0.0",
+        "gaxios": "^6.1.1",
         "gcp-metadata": "^6.1.0",
         "gtoken": "^7.0.0",
         "jws": "^4.0.0"
       }
     },
     "gtoken": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.0.1.tgz",
-      "integrity": "sha512-KcFVtoP1CVFtQu0aSk3AyAt2og66PFhZAlkUOuWKwzMLoulHXG5W5wE5xAnHb+yl3/wEFoqGW7/cDGMU8igDZQ==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
+      "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
       "requires": {
         "gaxios": "^6.0.0",
         "jws": "^4.0.0"
@@ -282,9 +282,9 @@
       }
     },
     "https-proxy-agent": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz",
-      "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==",
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz",
+      "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==",
       "requires": {
         "agent-base": "^7.0.2",
         "debug": "4"
@@ -390,12 +390,11 @@
       "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="
     },
     "retry-request": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.1.tgz",
-      "integrity": "sha512-ZI6vJp9rfB71mrZpw+n9p/B6HCsd7QJlSEQftZ+xfJzr3cQ9EPGKw1FF0BnViJ0fYREX6FhymBD2CARpmsFciQ==",
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz",
+      "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==",
       "requires": {
         "@types/request": "^2.48.8",
-        "debug": "^4.1.1",
         "extend": "^3.0.2",
         "teeny-request": "^9.0.0"
       }
@@ -414,9 +413,9 @@
       }
     },
     "stream-shift": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
-      "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ=="
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
+      "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ=="
     },
     "string_decoder": {
       "version": "1.3.0",
diff --git a/examples/node/package.json b/examples/node/package.json
index 98438f7..e167b33 100644
--- a/examples/node/package.json
+++ b/examples/node/package.json
@@ -10,6 +10,6 @@
   "author": "",
   "license": "ISC",
   "dependencies": {
-    "@google-cloud/storage": "^7.7.0"
+    "@google-cloud/storage": "^7.8.0"
   }
 }
diff --git a/fakestorage/json_response.go b/fakestorage/json_response.go
index f16a7c5..99e8ce7 100644
--- a/fakestorage/json_response.go
+++ b/fakestorage/json_response.go
@@ -32,7 +32,7 @@ func jsonToHTTPHandler(h jsonHandler) http.HandlerFunc {
 		status := resp.getStatus()
 		var data any
 		if status > 399 {
-			data = newErrorResponse(status, resp.getErrorMessage(status), nil)
+			data = newErrorResponse(status, resp.getErrorMessage(status), resp.getErrorList(status))
 		} else {
 			data = resp.data
 		}
@@ -59,6 +59,18 @@ func (r *jsonResponse) getErrorMessage(status int) string {
 	return http.StatusText(status)
 }
 
+func (r *jsonResponse) getErrorList(status int) []apiError {
+	if status == http.StatusOK {
+		return nil
+	} else {
+		return []apiError{{
+			Domain:  "global",
+			Reason:  http.StatusText(status),
+			Message: r.getErrorMessage(status),
+		}}
+	}
+}
+
 func errToJsonResponse(err error) jsonResponse {
 	status := 0
 	var pathError *os.PathError
diff --git a/fakestorage/object.go b/fakestorage/object.go
index c433a62..c7b4095 100644
--- a/fakestorage/object.go
+++ b/fakestorage/object.go
@@ -854,7 +854,7 @@ func (s *Server) downloadObject(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("X-Goog-Generation", strconv.FormatInt(obj.Generation, 10))
 	w.Header().Set("X-Goog-Hash", fmt.Sprintf("crc32c=%s,md5=%s", obj.Crc32c, obj.Md5Hash))
 	w.Header().Set("Last-Modified", obj.Updated.Format(http.TimeFormat))
-	w.Header().Set("ETag", obj.Etag)
+	w.Header().Set("ETag", fmt.Sprintf("%q", obj.Etag))
 	for name, value := range obj.Metadata {
 		w.Header().Set("X-Goog-Meta-"+name, value)
 	}
diff --git a/fakestorage/upload.go b/fakestorage/upload.go
index 3aaeb0f..c85de72 100644
--- a/fakestorage/upload.go
+++ b/fakestorage/upload.go
@@ -504,7 +504,7 @@ func (s *Server) uploadFileContent(r *http.Request) jsonResponse {
 	obj.Content = append(obj.Content, content...)
 	obj.Crc32c = checksum.EncodedCrc32cChecksum(obj.Content)
 	obj.Md5Hash = checksum.EncodedMd5Hash(obj.Content)
-	obj.Etag = fmt.Sprintf("%q", obj.Md5Hash)
+	obj.Etag = obj.Md5Hash
 	contentTypeHeader := r.Header.Get(contentTypeHeader)
 	if contentTypeHeader != "" {
 		obj.ContentType = contentTypeHeader
diff --git a/go.mod b/go.mod
index 4220b76..bda3573 100644
--- a/go.mod
+++ b/go.mod
@@ -1,7 +1,7 @@
 module github.com/fsouza/fake-gcs-server
 
 require (
-	cloud.google.com/go/pubsub v1.36.2
+	cloud.google.com/go/pubsub v1.37.0
 	cloud.google.com/go/storage v1.39.1
 	github.com/fsouza/slognil v0.4.0
 	github.com/google/go-cmp v0.6.0
@@ -11,7 +11,7 @@ require (
 	github.com/pkg/xattr v0.4.9
 	github.com/stretchr/testify v1.9.0
 	golang.org/x/oauth2 v0.18.0
-	google.golang.org/api v0.168.0
+	google.golang.org/api v0.169.0
 	google.golang.org/genproto/googleapis/api v0.0.0-20240304161311-37d4d3c04a78
 )
 
diff --git a/go.sum b/go.sum
index 47ba43e..5693cf3 100644
--- a/go.sum
+++ b/go.sum
@@ -9,8 +9,8 @@ cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc=
 cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI=
 cloud.google.com/go/kms v1.15.7 h1:7caV9K3yIxvlQPAcaFffhlT7d1qpxjB1wHBtjWa13SM=
 cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI=
-cloud.google.com/go/pubsub v1.36.2 h1:nAUD4aiWHZFYyINhRag1qOnHUk0/7QiWEa04XWnqACA=
-cloud.google.com/go/pubsub v1.36.2/go.mod h1:mHCFLNG8abCrPzhuOnpBcr9DUy+l3/LWWn0qoJdbh1w=
+cloud.google.com/go/pubsub v1.37.0 h1:0uEEfaB1VIJzabPpwpZf44zWAKAme3zwKKxHk7vJQxQ=
+cloud.google.com/go/pubsub v1.37.0/go.mod h1:YQOQr1uiUM092EXwKs56OPT650nwnawc+8/IjoUeGzQ=
 cloud.google.com/go/storage v1.39.1 h1:MvraqHKhogCOTXTlct/9C3K3+Uy2jBmFYb3/Sp6dVtY=
 cloud.google.com/go/storage v1.39.1/go.mod h1:xK6xZmxZmo+fyP7+DEF6FhNc24/JAe95OLyOHCXFH1o=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -194,8 +194,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
-google.golang.org/api v0.168.0 h1:MBRe+Ki4mMN93jhDDbpuRLjRddooArz4FeSObvUMmjY=
-google.golang.org/api v0.168.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg=
+google.golang.org/api v0.169.0 h1:QwWPy71FgMWqJN/l6jVlFHUa29a7dcUy02I8o799nPY=
+google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
diff --git a/internal/backend/fs.go b/internal/backend/fs.go
index 64f1106..c96867d 100644
--- a/internal/backend/fs.go
+++ b/internal/backend/fs.go
@@ -251,7 +251,7 @@ func (s *storageFS) CreateObject(obj StreamingObject, conditions Conditions) (St
 		obj.Md5Hash = hasher.EncodedMd5Hash()
 	}
 	if obj.Etag == "" {
-		obj.Etag = fmt.Sprintf("%q", obj.Md5Hash)
+		obj.Etag = obj.Md5Hash
 	}
 
 	// TODO: Handle if metadata is not present more gracefully?
diff --git a/internal/backend/memory.go b/internal/backend/memory.go
index 5075bf1..c32f06a 100644
--- a/internal/backend/memory.go
+++ b/internal/backend/memory.go
@@ -44,7 +44,7 @@ func (bm *bucketInMemory) addObject(obj Object) Object {
 		obj.Md5Hash = checksum.EncodedMd5Hash(obj.Content)
 	}
 	if obj.Etag == "" {
-		obj.Etag = fmt.Sprintf("%q", obj.Md5Hash)
+		obj.Etag = obj.Md5Hash
 	}
 	if obj.Size == 0 {
 		obj.Size = int64(len(obj.Content))


Test patch:
diff --git a/.github/workflows/docker-tests.yml b/.github/workflows/docker-tests.yml
index fc43ec0..6d8818a 100644
--- a/.github/workflows/docker-tests.yml
+++ b/.github/workflows/docker-tests.yml
@@ -19,7 +19,7 @@ jobs:
         uses: docker/setup-buildx-action@v3.1.0
 
       - name: build
-        uses: docker/build-push-action@v5.1.0
+        uses: docker/build-push-action@v5.2.0
         with:
           cache-from: fsouzaci/gha-cache:fake-gcs-server
           context: .
diff --git a/fakestorage/object_test.go b/fakestorage/object_test.go
index b158158..a943bf0 100644
--- a/fakestorage/object_test.go
+++ b/fakestorage/object_test.go
@@ -177,7 +177,7 @@ func checkObjectAttrs(testObj Object, attrs *storage.ObjectAttrs, t *testing.T)
 	if testObj.Content != nil && !bytes.Equal(attrs.MD5, checksum.MD5Hash(testObj.Content)) {
 		t.Errorf("wrong hash returned\nwant %d\ngot   %d", checksum.MD5Hash(testObj.Content), attrs.MD5)
 	}
-	expectedEtag := fmt.Sprintf("\"%s\"", checksum.EncodedHash(attrs.MD5))
+	expectedEtag := checksum.EncodedHash(attrs.MD5)
 	if attrs.Etag != expectedEtag {
 		t.Errorf("wrong Etag returned\nwant %s\ngot   %s", expectedEtag, attrs.Etag)
 	}
@@ -2028,7 +2028,7 @@ func TestServiceClientComposeObject(t *testing.T) {
 				"files/destination.txt",
 				[]string{"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33"},
 				"",
-				"googleapi: Error 400: The number of source components provided (33) exceeds the maximum (32)",
+				"googleapi: Error 400: The number of source components provided (33) exceeds the maximum (32), Bad Request",
 			},
 		}
 		for _, test := range tests {
diff --git a/fakestorage/upload_test.go b/fakestorage/upload_test.go
index 79908fa..582c913 100644
--- a/fakestorage/upload_test.go
+++ b/fakestorage/upload_test.go
@@ -224,7 +224,7 @@ func TestServerClientObjectWriterWithDoesNotExistPrecondition(t *testing.T) {
 		if err == nil {
 			t.Fatal("expected overwriting existing object to fail, but received no error")
 		}
-		if err.Error() != "googleapi: Error 412: Precondition failed" {
+		if err.Error() != "googleapi: Error 412: Precondition failed, Precondition Failed" {
 			t.Errorf("expected HTTP 412 precondition failed error, but got %v", err)
 		}
 
@@ -346,7 +346,7 @@ func TestServerClientObjectOperationsWithIfGenerationNotMatchPrecondition(t *tes
 		if err == nil {
 			t.Fatal("expected overwriting existing object to fail, but received no error")
 		}
-		if err.Error() != "googleapi: Error 412: Precondition failed" {
+		if err.Error() != "googleapi: Error 412: Precondition failed, Precondition Failed" {
 			t.Errorf("expected HTTP 412 precondition failed error, but got %v", err)
 		}
 
diff --git a/internal/backend/backend_test.go b/internal/backend/backend_test.go
index 80f2ed8..949b81a 100644
--- a/internal/backend/backend_test.go
+++ b/internal/backend/backend_test.go
@@ -294,7 +294,6 @@ func TestBucketAttrsStoreRetrieveUpdate(t *testing.T) {
 }
 
 func TestBucketCreateGetListDelete(t *testing.T) {
-	startTime := time.Now()
 	testForStorageBackends(t, func(t *testing.T, storage Storage) {
 		buckets, err := storage.ListBuckets()
 		if err != nil {
@@ -312,12 +311,9 @@ func TestBucketCreateGetListDelete(t *testing.T) {
 			if err == nil {
 				t.Fatalf("bucket %s, exists before being created", bucket.Name)
 			}
-			// The FS backend uses filesystem timestamps to store bucket creation time.
-			// Use a large +/- 5 second window to allow for an imperfectly synchronized
-			// clock generating the filesystem timestamp and to reduce test flakes.
-			timeBeforeCreation := time.Now().Add(-5 * time.Second)
+			timeBeforeCreation := time.Now()
 			err = storage.CreateBucket(bucket.Name, BucketAttrs{VersioningEnabled: bucket.VersioningEnabled})
-			timeAfterCreation := time.Now().Add(5 * time.Second)
+			timeAfterCreation := time.Now()
 			if reflect.TypeOf(storage) == reflect.TypeOf(&storageFS{}) && bucket.VersioningEnabled {
 				if err == nil {
 					t.Fatal("fs storage should not accept creating buckets with versioning, but it's not failing")
@@ -332,7 +328,7 @@ func TestBucketCreateGetListDelete(t *testing.T) {
 				t.Fatal(err)
 			}
 			if !isBucketEquivalentTo(bucketFromStorage, bucket, timeBeforeCreation, timeAfterCreation) {
-				t.Errorf("bucket %v does not have the expected props after retrieving. Expected %v and time between %v and %v",
+				t.Errorf("bucket %v does not have the expected props after retrieving. Expected %v and time roughly between %v and %v",
 					bucketFromStorage, bucket, timeBeforeCreation, timeAfterCreation)
 			}
 			buckets, err = storage.ListBuckets()
@@ -345,8 +341,8 @@ func TestBucketCreateGetListDelete(t *testing.T) {
 			if buckets[0].Name != bucket.Name {
 				t.Errorf("listed bucket has unexpected name. Expected %s, actual: %v", bucket.Name, buckets[0].Name)
 			}
-			if buckets[0].TimeCreated.Before(startTime.Truncate(time.Second)) || time.Now().Before(buckets[0].TimeCreated) {
-				t.Errorf("listed bucket has unexpected creation time. Expected between test start time %v and now %v, actual: %v", startTime, time.Now(), buckets[0].TimeCreated)
+			if !isTimeRoughlyInRange(buckets[0].TimeCreated, timeBeforeCreation, timeAfterCreation) {
+				t.Errorf("listed bucket has unexpected creation time. Expected roughly between %v and %v, actual: %v", timeBeforeCreation, timeAfterCreation, buckets[0].TimeCreated)
 			}
 			err = storage.DeleteBucket(bucket.Name)
 			if err != nil {
@@ -356,10 +352,19 @@ func TestBucketCreateGetListDelete(t *testing.T) {
 	})
 }
 
-func isBucketEquivalentTo(a, b Bucket, earliest, latest time.Time) bool {
+func isBucketEquivalentTo(a, b Bucket, before, after time.Time) bool {
 	return a.Name == b.Name &&
 		a.VersioningEnabled == b.VersioningEnabled &&
-		a.TimeCreated.After(earliest) && a.TimeCreated.Before(latest)
+		isTimeRoughlyInRange(a.TimeCreated, before, after)
+}
+
+func isTimeRoughlyInRange(t, before, after time.Time) bool {
+	// The FS backend uses filesystem timestamps to store bucket creation time.
+	// Use a large +/- 5 second window to allow for an imperfectly synchronized
+	// clock generating the filesystem timestamp and to reduce test flakes.
+	earliest := before.Add(-5 * time.Second)
+	latest := after.Add(5 * time.Second)
+	return t.After(earliest) && t.Before(latest)
 }
 
 func TestBucketDuplication(t *testing.T) {