You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
112 lines
3.4 KiB
112 lines
3.4 KiB
package lifecycle
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3lifecycle"
|
|
)
|
|
|
|
// makeVersionId creates a new-format version ID from a timestamp.
|
|
func makeVersionId(t time.Time) string {
|
|
inverted := math.MaxInt64 - t.UnixNano()
|
|
return fmt.Sprintf("%016x", inverted) + "0000000000000000"
|
|
}
|
|
|
|
func TestSortVersionsByVersionId(t *testing.T) {
|
|
t1 := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
t2 := time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC)
|
|
t3 := time.Date(2026, 3, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
vid1 := makeVersionId(t1)
|
|
vid2 := makeVersionId(t2)
|
|
vid3 := makeVersionId(t3)
|
|
|
|
entries := []*filer_pb.Entry{
|
|
{Name: "v_" + vid1},
|
|
{Name: "v_" + vid3},
|
|
{Name: "v_" + vid2},
|
|
}
|
|
|
|
sortVersionsByVersionId(entries)
|
|
|
|
// Should be sorted newest first: t3, t2, t1.
|
|
expected := []string{"v_" + vid3, "v_" + vid2, "v_" + vid1}
|
|
for i, want := range expected {
|
|
if entries[i].Name != want {
|
|
t.Errorf("entries[%d].Name = %s, want %s", i, entries[i].Name, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSortVersionsByVersionId_SameTimestampDifferentSuffix(t *testing.T) {
|
|
// Two versions with the same timestamp prefix but different random suffix.
|
|
// The sort must still produce a deterministic order.
|
|
base := makeVersionId(time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC))
|
|
vid1 := base[:16] + "aaaaaaaaaaaaaaaa"
|
|
vid2 := base[:16] + "bbbbbbbbbbbbbbbb"
|
|
|
|
entries := []*filer_pb.Entry{
|
|
{Name: "v_" + vid2},
|
|
{Name: "v_" + vid1},
|
|
}
|
|
|
|
sortVersionsByVersionId(entries)
|
|
|
|
// New format: smaller hex = newer. vid1 ("aaa...") < vid2 ("bbb...") so vid1 is newer.
|
|
if strings.TrimPrefix(entries[0].Name, "v_") != vid1 {
|
|
t.Errorf("expected vid1 (newer) first, got %s", entries[0].Name)
|
|
}
|
|
}
|
|
|
|
func TestCompareVersionIdsMixedFormats(t *testing.T) {
|
|
// Old format: raw nanosecond timestamp (below threshold ~0x17...).
|
|
// New format: inverted timestamp (above threshold ~0x68...).
|
|
oldTs := time.Date(2023, 6, 15, 12, 0, 0, 0, time.UTC)
|
|
newTs := time.Date(2026, 3, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
oldFormatId := fmt.Sprintf("%016x", oldTs.UnixNano()) + "abcdef0123456789"
|
|
newFormatId := makeVersionId(newTs) // uses inverted timestamp
|
|
|
|
// newTs is more recent, so newFormatId should sort as "newer".
|
|
cmp := s3lifecycle.CompareVersionIds(newFormatId, oldFormatId)
|
|
if cmp >= 0 {
|
|
t.Errorf("expected new-format ID (2026) to be newer than old-format ID (2023), got cmp=%d", cmp)
|
|
}
|
|
|
|
// Reverse comparison.
|
|
cmp2 := s3lifecycle.CompareVersionIds(oldFormatId, newFormatId)
|
|
if cmp2 <= 0 {
|
|
t.Errorf("expected old-format ID (2023) to be older than new-format ID (2026), got cmp=%d", cmp2)
|
|
}
|
|
|
|
// Sort a mixed slice: should be newest-first.
|
|
entries := []*filer_pb.Entry{
|
|
{Name: "v_" + oldFormatId},
|
|
{Name: "v_" + newFormatId},
|
|
}
|
|
sortVersionsByVersionId(entries)
|
|
|
|
if strings.TrimPrefix(entries[0].Name, "v_") != newFormatId {
|
|
t.Errorf("expected new-format (newer) entry first after sort")
|
|
}
|
|
}
|
|
|
|
func TestVersionsDirectoryNaming(t *testing.T) {
|
|
if s3_constants.VersionsFolder != ".versions" {
|
|
t.Fatalf("unexpected VersionsFolder constant: %q", s3_constants.VersionsFolder)
|
|
}
|
|
|
|
versionsDir := "/buckets/mybucket/path/to/key.versions"
|
|
bucketPath := "/buckets/mybucket"
|
|
relDir := strings.TrimPrefix(versionsDir, bucketPath+"/")
|
|
objKey := strings.TrimSuffix(relDir, s3_constants.VersionsFolder)
|
|
if objKey != "path/to/key" {
|
|
t.Errorf("expected 'path/to/key', got %q", objKey)
|
|
}
|
|
}
|