| // Copyright 2026 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package api | |
| import ( | |
| "testing" | |
| "time" | |
| ) | |
| func TestPageToken(t *testing.T) { | |
| t.Setenv("K_SERVICE", "test-service") | |
| tests := []int{0, 1, 42, 100, 999999} | |
| for _, n := range tests { | |
| encoded, err := encodePageToken(n) | |
| if err != nil { | |
| t.Fatalf("encodePageToken(%d): %v", n, err) | |
| } | |
| t.Logf("encoded token for %d: %s", n, encoded) | |
| decoded, err := decodePageToken(encoded) | |
| if err != nil { | |
| t.Fatalf("decodePageToken(%s): %v", encoded, err) | |
| } | |
| if decoded != n { | |
| t.Errorf("decoded = %d, want %d", decoded, n) | |
| } | |
| } | |
| } | |
| func TestDecodePageTokenInvalid(t *testing.T) { | |
| t.Setenv("K_SERVICE", "test-service") | |
| invalidTokens := []string{ | |
| "not-hex", | |
| "1234", // too short | |
| "00000000000000000000000000000000", // garbage decrypted bytes | |
| } | |
| for _, token := range invalidTokens { | |
| if _, err := decodePageToken(token); err == nil { | |
| t.Errorf("decodePageToken(%s) succeeded, want error", token) | |
| } | |
| } | |
| // Test expired token (49 hours ago). | |
| expiredTime := time.Now().Add(-49 * time.Hour) | |
| expiredToken, err := encodePageToken1(42, expiredTime) | |
| if err != nil { | |
| t.Fatalf("encodePageToken1: %v", err) | |
| } | |
| if _, err := decodePageToken(expiredToken); err == nil { | |
| t.Error("decodePageToken succeeded for expired token, want error") | |
| } else if want := "expired"; err.Error() != want { | |
| t.Errorf("decodePageToken error = %v, want %s", err, want) | |
| } | |
| // Test future token (2 minutes in the future). | |
| futureTime := time.Now().Add(2 * time.Minute) | |
| futureToken, err := encodePageToken1(42, futureTime) | |
| if err != nil { | |
| t.Fatalf("encodePageToken1: %v", err) | |
| } | |
| if _, err := decodePageToken(futureToken); err == nil { | |
| t.Error("decodePageToken succeeded for future token, want error") | |
| } else if want := "from the future"; err.Error() != want { | |
| t.Errorf("decodePageToken error = %v, want %s", err, want) | |
| } | |
| // Test overly large offset (1,000,000). | |
| largeOffsetToken, err := encodePageToken1(maxOffset+1, time.Now()) | |
| if err != nil { | |
| t.Fatalf("encodePageToken1: %v", err) | |
| } | |
| if _, err := decodePageToken(largeOffsetToken); err == nil { | |
| t.Error("decodePageToken succeeded for large offset token, want error") | |
| } else if want := "offset too large"; err.Error() != want { | |
| t.Errorf("decodePageToken error = %v, want %s", err, want) | |
| } | |
| } | |
| func TestStringPageToken(t *testing.T) { | |
| t.Setenv("K_SERVICE", "test-service") | |
| tests := []string{ | |
| "", | |
| "a", | |
| "hello", | |
| "github.com/google/go-cmp/cmp", | |
| "golang.org/x/pkgsite/internal/postgres/details_test", | |
| "a/very/long/path/that/exceeds/the/aes/block/size/and/requires/gcm/to/encrypt/properly/without/truncation/or/errors/because/it/is/long", | |
| } | |
| for _, s := range tests { | |
| encoded, err := encodeStringPageToken(s) | |
| if err != nil { | |
| t.Fatalf("encodeStringPageToken(%q): %v", s, err) | |
| } | |
| t.Logf("encoded string token for %q: %s (len=%d)", s, encoded, len(encoded)) | |
| decoded, err := decodeStringPageToken(encoded) | |
| if err != nil { | |
| t.Fatalf("decodeStringPageToken(%s): %v", encoded, err) | |
| } | |
| if decoded != s { | |
| t.Errorf("decoded = %q, want %q", decoded, s) | |
| } | |
| } | |
| } | |
| func TestDecodeStringPageTokenInvalid(t *testing.T) { | |
| t.Setenv("K_SERVICE", "test-service") | |
| invalidTokens := []string{ | |
| "not-hex", | |
| "1234", // too short | |
| "00000000000000000000000000000000", // garbage decrypted bytes | |
| } | |
| for _, token := range invalidTokens { | |
| if _, err := decodeStringPageToken(token); err == nil { | |
| t.Errorf("decodeStringPageToken(%s) succeeded, want error", token) | |
| } | |
| } | |
| // Test expired token (49 hours ago). | |
| expiredTime := time.Now().Add(-tokenExpiry) | |
| expiredToken, err := encodeStringPageToken1("expired-test", expiredTime) | |
| if err != nil { | |
| t.Fatalf("encodeStringPageToken1: %v", err) | |
| } | |
| if _, err := decodeStringPageToken(expiredToken); err == nil { | |
| t.Error("decodeStringPageToken succeeded for expired token, want error") | |
| } else if want := "expired"; err.Error() != want { | |
| t.Errorf("decodeStringPageToken error = %v, want %s", err, want) | |
| } | |
| // Test future token (2 minutes in the future). | |
| futureTime := time.Now().Add(2 * time.Minute) | |
| futureToken, err := encodeStringPageToken1("future-test", futureTime) | |
| if err != nil { | |
| t.Fatalf("encodeStringPageToken1: %v", err) | |
| } | |
| if _, err := decodeStringPageToken(futureToken); err == nil { | |
| t.Error("decodeStringPageToken succeeded for future token, want error") | |
| } else if want := "from the future"; err.Error() != want { | |
| t.Errorf("decodeStringPageToken error = %v, want %s", err, want) | |
| } | |
| } |