@@ -58,7 +58,7 @@ func TestDecode(t *testing.T) {
5858 assert .True (t , c .IsEmpty ())
5959 })
6060
61- t .Run ("decodes valid cursor" , func (t * testing.T ) {
61+ t .Run ("decodes v1 cursor" , func (t * testing.T ) {
6262 original := Cursor {
6363 SortBy : "delivery_time" ,
6464 SortOrder : "desc" ,
@@ -92,40 +92,110 @@ func TestDecode(t *testing.T) {
9292 assert .True (t , errors .Is (err , driver .ErrInvalidCursor ))
9393 })
9494
95- t .Run ("invalid format returns error" , func (t * testing.T ) {
96- // Encode something that's not in the right format
97- _ , err := Decode ("abc123" )
95+ t .Run ("v1 invalid sortBy returns error" , func (t * testing.T ) {
96+ raw := "v1:invalid_sort:desc:position"
97+ encoded := encodeRaw (raw )
98+
99+ _ , err := Decode (encoded )
98100 require .Error (t , err )
99101 assert .True (t , errors .Is (err , driver .ErrInvalidCursor ))
100102 })
101103
102- t .Run ("invalid sortBy returns error" , func (t * testing.T ) {
103- // Manually create a cursor with invalid sortBy by encoding raw bytes
104- raw := "v1:invalid_sort:desc:position"
104+ t .Run ("v1 invalid sortOrder returns error" , func (t * testing.T ) {
105+ raw := "v1:event_time:invalid_order:position"
105106 encoded := encodeRaw (raw )
106107
107108 _ , err := Decode (encoded )
108109 require .Error (t , err )
109110 assert .True (t , errors .Is (err , driver .ErrInvalidCursor ))
110111 })
111112
112- t .Run ("invalid sortOrder returns error" , func (t * testing.T ) {
113- raw := "v1:event_time:invalid_order:position "
113+ t .Run ("v1 empty position returns error" , func (t * testing.T ) {
114+ raw := "v1:event_time:desc: "
114115 encoded := encodeRaw (raw )
115116
116117 _ , err := Decode (encoded )
117118 require .Error (t , err )
118119 assert .True (t , errors .Is (err , driver .ErrInvalidCursor ))
119120 })
120121
121- t .Run ("unsupported version returns error" , func (t * testing.T ) {
122- raw := "v99 :event_time:desc:position"
122+ t .Run ("v1 missing parts returns error" , func (t * testing.T ) {
123+ raw := "v1 :event_time:desc" // missing position
123124 encoded := encodeRaw (raw )
124125
125126 _ , err := Decode (encoded )
126127 require .Error (t , err )
127128 assert .True (t , errors .Is (err , driver .ErrInvalidCursor ))
128- assert .Contains (t , err .Error (), "unsupported cursor version" )
129+ })
130+ }
131+
132+ func TestDecodeV0BackwardCompatibility (t * testing.T ) {
133+ t .Run ("decodes v0 cursor with defaults" , func (t * testing.T ) {
134+ // v0 format: just position, no version prefix
135+ position := "1704067200_evt_abc"
136+ encoded := encodeRaw (position )
137+
138+ decoded , err := Decode (encoded )
139+ require .NoError (t , err )
140+ assert .Equal (t , position , decoded .Position )
141+ assert .Equal (t , "event_time" , decoded .SortBy , "v0 defaults to event_time" )
142+ assert .Equal (t , "desc" , decoded .SortOrder , "v0 defaults to desc" )
143+ })
144+
145+ t .Run ("decodes v0 composite cursor" , func (t * testing.T ) {
146+ // v0 composite cursor for event_time sort
147+ position := "1704067200_evt_abc_1704067500_del_xyz"
148+ encoded := encodeRaw (position )
149+
150+ decoded , err := Decode (encoded )
151+ require .NoError (t , err )
152+ assert .Equal (t , position , decoded .Position )
153+ assert .Equal (t , "event_time" , decoded .SortBy )
154+ assert .Equal (t , "desc" , decoded .SortOrder )
155+ })
156+
157+ t .Run ("v0 cursor validates with matching defaults" , func (t * testing.T ) {
158+ position := "1704067200_evt_abc"
159+ encoded := encodeRaw (position )
160+
161+ // Should work with default sort params
162+ next , _ , err := DecodeAndValidate (encoded , "" , "event_time" , "desc" )
163+ require .NoError (t , err )
164+ assert .Equal (t , position , next .Position )
165+ })
166+
167+ t .Run ("v0 cursor fails validation with non-default sort params" , func (t * testing.T ) {
168+ position := "1704067200_del_xyz"
169+ encoded := encodeRaw (position )
170+
171+ // Should fail because v0 defaults to event_time, not delivery_time
172+ _ , _ , err := DecodeAndValidate (encoded , "" , "delivery_time" , "desc" )
173+ require .Error (t , err )
174+ assert .True (t , errors .Is (err , driver .ErrInvalidCursor ))
175+ assert .Contains (t , err .Error (), "sortBy" )
176+ })
177+
178+ t .Run ("v0 cursor fails validation with different sort order" , func (t * testing.T ) {
179+ position := "1704067200_evt_abc"
180+ encoded := encodeRaw (position )
181+
182+ // Should fail because v0 defaults to desc, not asc
183+ _ , _ , err := DecodeAndValidate (encoded , "" , "event_time" , "asc" )
184+ require .Error (t , err )
185+ assert .True (t , errors .Is (err , driver .ErrInvalidCursor ))
186+ assert .Contains (t , err .Error (), "sortOrder" )
187+ })
188+
189+ t .Run ("random string treated as v0 position" , func (t * testing.T ) {
190+ // Any valid base62 that doesn't start with "v1:" is treated as v0
191+ position := "some_random_position_string"
192+ encoded := encodeRaw (position )
193+
194+ decoded , err := Decode (encoded )
195+ require .NoError (t , err )
196+ assert .Equal (t , position , decoded .Position )
197+ assert .Equal (t , "event_time" , decoded .SortBy )
198+ assert .Equal (t , "desc" , decoded .SortOrder )
129199 })
130200}
131201
@@ -240,7 +310,7 @@ func TestRoundTrip(t *testing.T) {
240310 }
241311}
242312
243- // encodeRaw is a helper to encode raw strings for testing invalid formats
313+ // encodeRaw is a helper to encode raw strings for testing
244314func encodeRaw (raw string ) string {
245315 num := new (big.Int )
246316 num .SetBytes ([]byte (raw ))
0 commit comments