)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"3fffecdd061839f751271522e2f80dd0a30046b4","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":7,"id":"4c040cde_05e81803","updated":"2026-05-06 15:21:55.000000000","message":"I forgot this object-server has this interface, and I\u0027m not 100% I understand the justification in the original change, but since we\u0027re going to maintain this behavior we might as well do it proper.\n\nThis rewording of existing behavior is shorter and more idiomatic, the test additions make sense and fail as expected.  Change LGTM.","commit_id":"ae89a535aadad6d77c07c12ef4b58eb4470decdb"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"20507118b3bd7facf744f9ed732d6e26cf4e7a99","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":7,"id":"0652aae7_0e12dfef","updated":"2026-05-06 16:24:34.000000000","message":"recheck\n\nFailure in `tempest.scenario.test_server_basic_ops.TestServerBasicOps.test_server_basic_ops` which seems to be testing Nova; nothing to do with us.","commit_id":"ae89a535aadad6d77c07c12ef4b58eb4470decdb"}],"swift/obj/server.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"3fffecdd061839f751271522e2f80dd0a30046b4","unresolved":true,"context_lines":[{"line_number":1117,"context_line":"        timing_stats_labels[\u0027container\u0027] \u003d container"},{"line_number":1118,"context_line":"        timing_stats_labels[\u0027policy\u0027] \u003d int(policy)"},{"line_number":1119,"context_line":""},{"line_number":1120,"context_line":"        req_timestamp \u003d request.ensure_x_timestamp()"},{"line_number":1121,"context_line":"        frag_prefs \u003d safe_json_loads("},{"line_number":1122,"context_line":"            request.headers.get(\u0027X-Backend-Fragment-Preferences\u0027))"},{"line_number":1123,"context_line":"        try:"}],"source_content_type":"text/x-python","patch_set":7,"id":"b8269646_705f4998","line":1120,"updated":"2026-05-06 15:21:55.000000000","message":"seems good to me!\n\n```\n    def ensure_x_timestamp(self):\n        \"\"\"\n        Similar to :attr:`timestamp`, but the ``X-Timestamp`` header will be\n        set if not present.\n\n        :raises HTTPBadRequest: if X-Timestamp is already set but not a valid\n                                :class:`~swift.common.utils.Timestamp`\n        :returns: the request\u0027s X-Timestamp header,\n                  as a :class:`~swift.common.utils.Timestamp`\n        \"\"\"\n        # The container sync feature includes an x-timestamp header with\n        # requests. If present this is checked and preserved, otherwise a fresh\n        # timestamp is added.\n        if \u0027HTTP_X_TIMESTAMP\u0027 in self.environ:\n            try:\n                self._timestamp \u003d Timestamp(self.environ[\u0027HTTP_X_TIMESTAMP\u0027])\n            except ValueError:\n                raise HTTPBadRequest(\n                    request\u003dself, content_type\u003d\u0027text/plain\u0027,\n                    body\u003d\u0027X-Timestamp should be a UNIX timestamp float value; \u0027\n                         \u0027was %r\u0027 % self.environ[\u0027HTTP_X_TIMESTAMP\u0027])\n        else:\n            self._timestamp \u003d Timestamp.now()\n        # Always normalize it to the internal form\n        self.environ[\u0027HTTP_X_TIMESTAMP\u0027] \u003d self._timestamp.internal\n        return self._timestamp\n```","commit_id":"ae89a535aadad6d77c07c12ef4b58eb4470decdb"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"3fffecdd061839f751271522e2f80dd0a30046b4","unresolved":true,"context_lines":[{"line_number":1216,"context_line":"        except DiskFileDeviceUnavailable:"},{"line_number":1217,"context_line":"            return HTTPInsufficientStorage(drive\u003ddevice, request\u003drequest)"},{"line_number":1218,"context_line":"        try:"},{"line_number":1219,"context_line":"            metadata \u003d disk_file.read_metadata(current_time\u003dreq_timestamp)"},{"line_number":1220,"context_line":"        except DiskFileXattrNotSupported:"},{"line_number":1221,"context_line":"            return HTTPInsufficientStorage(drive\u003ddevice, request\u003drequest)"},{"line_number":1222,"context_line":"        except (DiskFileNotExist, DiskFileQuarantined) as e:"}],"source_content_type":"text/x-python","patch_set":7,"id":"a95c48e3_aeab8d69","line":1219,"updated":"2026-05-06 15:21:55.000000000","message":"so it seems like we\u0027ve been doing this since I10c28f97d4c6aca1d64bef3b93506cfbb50ade30 \"Use X-Timestamp when checking object expiration\"\n\n\u003e It even opens a (weird? useful?) API by which to open expired objects (!?)\n\n^ I didn\u0027t think of that!?\n\n```\nvagrant@saio:~$ curl http://127.0.0.2:6020/sdb2/645/AUTH_test/test/test -I\nHTTP/1.1 404 Not Found\nContent-Type: text/html; charset\u003dUTF-8\nX-Backend-Timestamp: 1778080301.33537\nDate: Wed, 06 May 2026 15:19:28 GMT\n\nvagrant@saio:~$ curl http://127.0.0.2:6020/sdb2/645/AUTH_test/test/test -I -H \u0027x-timestamp: 0\u0027\nHTTP/1.1 200 OK\nContent-Type: application/octet-stream\nX-Object-Meta-Mtime: 1775585884.552330\nX-Delete-At: 1778080302\nEtag: \"70c1db56f301c9e337b0099bd4174b28\"\nLast-Modified: Wed, 06 May 2026 15:11:42 GMT\nX-Timestamp: 1778080301.33537\nX-Backend-Timestamp: 1778080301.33537\nX-Backend-Data-Timestamp: 1778080301.33537\nX-Backend-Durable-Timestamp: 1778080301.33537\nContent-Length: 8\nDate: Wed, 06 May 2026 15:19:30 GMT\n\n```","commit_id":"ae89a535aadad6d77c07c12ef4b58eb4470decdb"}],"test/unit/obj/test_server.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"3fffecdd061839f751271522e2f80dd0a30046b4","unresolved":true,"context_lines":[{"line_number":3886,"context_line":"        self.assertEqual(resp.status_int, 400)"},{"line_number":3887,"context_line":"        self.assertEqual("},{"line_number":3888,"context_line":"            b\"X-Timestamp should be a UNIX timestamp float value; was \u0027bad\u0027\","},{"line_number":3889,"context_line":"            resp.body)"},{"line_number":3890,"context_line":""},{"line_number":3891,"context_line":"        req \u003d Request.blank("},{"line_number":3892,"context_line":"            \u0027/sda1/p/a/c/o\u0027,"}],"source_content_type":"text/x-python","patch_set":7,"id":"7b9fb7cd_1d8034d0","line":3889,"updated":"2026-05-06 15:21:55.000000000","message":"looks like the 400 status is existing behavior, but our (internal) error message improved:\n\n```\nFAILED swift/test/unit/obj/test_server.py::TestObjectController::test_GET_invalid_timestamp - AssertionError: b\"X-Timestamp should be a UNIX timestamp float value; was \u0027bad\u0027\" !\u003d b\u0027Invalid X-Timestamp header\u0027\n```\n\n^ with change reverted","commit_id":"ae89a535aadad6d77c07c12ef4b58eb4470decdb"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"3fffecdd061839f751271522e2f80dd0a30046b4","unresolved":true,"context_lines":[{"line_number":7815,"context_line":"        with mock_timestamp_now(ts_req):"},{"line_number":7816,"context_line":"            resp \u003d req.get_response(self.object_controller)"},{"line_number":7817,"context_line":"        self.assertEqual(resp.status_int, 200)"},{"line_number":7818,"context_line":"        self.assertEqual(ts_req, req.timestamp)"},{"line_number":7819,"context_line":""},{"line_number":7820,"context_line":"        # expired..."},{"line_number":7821,"context_line":"        req \u003d Request.blank("}],"source_content_type":"text/x-python","patch_set":7,"id":"2c17ab85_bdac953c","line":7818,"updated":"2026-05-06 15:21:55.000000000","message":"the move from `time.time` to `Timestamp.now` means the mock doesn\u0027t work on master:\n\n```\nFAILED swift/test/unit/obj/test_server.py::TestObjectController::test_GET_but_expired_server_provides_x_timestamp - AssertionError: 1778079844.00000_0000000000000000 !\u003d 1778079745.67911_0000000000000000\n```\n\nwith a minor change this test asserts existing behavior:\n\n````\ndiff --git a/test/unit/obj/test_server.py b/test/unit/obj/test_server.py\nindex 4c6a679078..4ca506673d 100644\n--- a/test/unit/obj/test_server.py\n+++ b/test/unit/obj/test_server.py\n@@ -7812,7 +7812,7 @@ class TestObjectController(BaseUnitTestCase):\n             \u0027/sda1/p/a/c/o\u0027, environ\u003d{\u0027REQUEST_METHOD\u0027: \u0027GET\u0027},\n             headers\u003d{})\n         ts_req \u003d Timestamp(float(ts_now), delta\u003d99 * 1e5)\n-        with mock_timestamp_now(ts_req):\n+        with mock.patch(\u0027time.time\u0027, return_value\u003dfloat(ts_req)):\n             resp \u003d req.get_response(self.object_controller)\n         self.assertEqual(resp.status_int, 200)\n         self.assertEqual(ts_req, req.timestamp)\n@@ -7822,7 +7822,7 @@ class TestObjectController(BaseUnitTestCase):\n             \u0027/sda1/p/a/c/o\u0027, environ\u003d{\u0027REQUEST_METHOD\u0027: \u0027GET\u0027},\n             headers\u003d{})\n         ts_req \u003d Timestamp(float(ts_now), delta\u003d101 * 1e5)\n-        with mock_timestamp_now(ts_req):\n+        with mock.patch(\u0027time.time\u0027, return_value\u003dfloat(ts_req)):\n             resp \u003d req.get_response(self.object_controller)\n         self.assertEqual(resp.status_int, 404)\n         self.assertEqual(ts_req, req.timestamp)\n```\n\nthe original change had some test churn, but didn\u0027t obviously explicitly test this behavior (or the new diskfile open interface?!) so this seems like a nice addition; KUDOS.","commit_id":"ae89a535aadad6d77c07c12ef4b58eb4470decdb"}]}
