fix potential endless loop during queue msg/hook pagination when environment has TZ UTC, triggered by tests introduced in previous test

time.Now() returns a timestamp with timezone Local. if you marshal & unmarshal
it again, it'll get the Local timezone again. unless the local timezone is UTC.
then it will get the UTC timezone. the same time.Time but with explicit UTC
timezone vs explicit UTC-as-Local timezone are not the same when comparing with
==. so comparison should be done with time.Time.Equal, or comparison should be
done after having called .Local() on parsed timestamps (so the explicit UTC
timezone gets converted to the UTC-as-Local timezone). somewhat surprising that
time.Local isn't the same as time.UTC if TZ=/TZ=UTC. there are warnings
throughout the time package about handling of UTC.
This commit is contained in:
Mechiel Lukkien 2024-04-16 13:58:08 +02:00
parent 09fcc49223
commit daa88480cb
No known key found for this signature in database
4 changed files with 10 additions and 9 deletions

View file

@ -309,7 +309,7 @@ done
- Update features & roadmap in README.md - Update features & roadmap in README.md
- Write release notes, copy from previous. - Write release notes, copy from previous.
- Build and run tests with previous major Go release. - Build and run tests with previous major Go release.
- Run tests, including with race detector. - Run tests, including with race detector, also with TZ= for UTC-behaviour.
- Run integration and upgrade tests. - Run integration and upgrade tests.
- Run fuzzing tests for a while. - Run fuzzing tests for a while.
- Deploy to test environment. Test the update instructions. - Deploy to test environment. Test the update instructions.

View file

@ -313,9 +313,9 @@ func (s HookSort) apply(q *bstore.Query[Hook]) error {
q.FilterNotEqual("ID", s.LastID) q.FilterNotEqual("ID", s.LastID)
var fieldEqual func(h Hook) bool var fieldEqual func(h Hook) bool
if s.Field == "NextAttempt" { if s.Field == "NextAttempt" {
fieldEqual = func(h Hook) bool { return h.NextAttempt == last } fieldEqual = func(h Hook) bool { return h.NextAttempt.Equal(last) }
} else { } else {
fieldEqual = func(h Hook) bool { return h.Submitted == last } fieldEqual = func(h Hook) bool { return h.Submitted.Equal(last) }
} }
if s.Asc { if s.Asc {
q.FilterGreaterEqual(s.Field, last) q.FilterGreaterEqual(s.Field, last)
@ -454,9 +454,9 @@ func (s HookRetiredSort) apply(q *bstore.Query[HookRetired]) error {
q.FilterNotEqual("ID", s.LastID) q.FilterNotEqual("ID", s.LastID)
var fieldEqual func(hr HookRetired) bool var fieldEqual func(hr HookRetired) bool
if s.Field == "LastActivity" { if s.Field == "LastActivity" {
fieldEqual = func(hr HookRetired) bool { return hr.LastActivity == last } fieldEqual = func(hr HookRetired) bool { return hr.LastActivity.Equal(last) }
} else { } else {
fieldEqual = func(hr HookRetired) bool { return hr.Submitted == last } fieldEqual = func(hr HookRetired) bool { return hr.Submitted.Equal(last) }
} }
if s.Asc { if s.Asc {
q.FilterGreaterEqual(s.Field, last) q.FilterGreaterEqual(s.Field, last)

View file

@ -82,6 +82,7 @@ func TestHookIncoming(t *testing.T) {
dec := json.NewDecoder(strings.NewReader(h.Payload)) dec := json.NewDecoder(strings.NewReader(h.Payload))
err = dec.Decode(&in) err = dec.Decode(&in)
tcheck(t, err, "decode incoming webhook") tcheck(t, err, "decode incoming webhook")
in.Meta.Received = in.Meta.Received.Local() // For TZ UTC.
expIncoming := webhook.Incoming{ expIncoming := webhook.Incoming{
From: []webhook.NameAddress{{Address: "mjl@mox.example"}}, From: []webhook.NameAddress{{Address: "mjl@mox.example"}},

View file

@ -492,9 +492,9 @@ func (s Sort) apply(q *bstore.Query[Msg]) error {
q.FilterNotEqual("ID", s.LastID) q.FilterNotEqual("ID", s.LastID)
var fieldEqual func(m Msg) bool var fieldEqual func(m Msg) bool
if s.Field == "NextAttempt" { if s.Field == "NextAttempt" {
fieldEqual = func(m Msg) bool { return m.NextAttempt == last } fieldEqual = func(m Msg) bool { return m.NextAttempt.Equal(last) }
} else { } else {
fieldEqual = func(m Msg) bool { return m.Queued == last } fieldEqual = func(m Msg) bool { return m.Queued.Equal(last) }
} }
if s.Asc { if s.Asc {
q.FilterGreaterEqual(s.Field, last) q.FilterGreaterEqual(s.Field, last)
@ -1015,9 +1015,9 @@ func (s RetiredSort) apply(q *bstore.Query[MsgRetired]) error {
q.FilterNotEqual("ID", s.LastID) q.FilterNotEqual("ID", s.LastID)
var fieldEqual func(m MsgRetired) bool var fieldEqual func(m MsgRetired) bool
if s.Field == "LastActivity" { if s.Field == "LastActivity" {
fieldEqual = func(m MsgRetired) bool { return m.LastActivity == last } fieldEqual = func(m MsgRetired) bool { return m.LastActivity.Equal(last) }
} else { } else {
fieldEqual = func(m MsgRetired) bool { return m.Queued == last } fieldEqual = func(m MsgRetired) bool { return m.Queued.Equal(last) }
} }
if s.Asc { if s.Asc {
q.FilterGreaterEqual(s.Field, last) q.FilterGreaterEqual(s.Field, last)