6 changed files with 307 additions and 10 deletions
-
2weed/s3api/bucket_metadata.go
-
82weed/s3api/bucket_paths.go
-
45weed/s3api/bucket_paths_test.go
-
83weed/s3api/s3tables/handler_table.go
-
77weed/s3api/s3tables/table_location_mapping_test.go
-
28weed/s3api/s3tables/utils.go
@ -0,0 +1,45 @@ |
|||
package s3api |
|||
|
|||
import "testing" |
|||
|
|||
func TestNormalizeTableLocationMappingPath(t *testing.T) { |
|||
testCases := []struct { |
|||
name string |
|||
raw string |
|||
want string |
|||
}{ |
|||
{ |
|||
name: "legacy table path maps to bucket root", |
|||
raw: "/buckets/warehouse/analytics/orders", |
|||
want: "/buckets/warehouse", |
|||
}, |
|||
{ |
|||
name: "already bucket root", |
|||
raw: "/buckets/warehouse", |
|||
want: "/buckets/warehouse", |
|||
}, |
|||
{ |
|||
name: "relative buckets path normalized and reduced", |
|||
raw: "buckets/warehouse/analytics/orders", |
|||
want: "/buckets/warehouse", |
|||
}, |
|||
{ |
|||
name: "non buckets path preserved", |
|||
raw: "/tmp/custom/path", |
|||
want: "/tmp/custom/path", |
|||
}, |
|||
{ |
|||
name: "empty path", |
|||
raw: "", |
|||
want: "", |
|||
}, |
|||
} |
|||
|
|||
for _, tc := range testCases { |
|||
t.Run(tc.name, func(t *testing.T) { |
|||
if got := normalizeTableLocationMappingPath(tc.raw); got != tc.want { |
|||
t.Fatalf("normalizeTableLocationMappingPath(%q)=%q, want %q", tc.raw, got, tc.want) |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
package s3tables |
|||
|
|||
import ( |
|||
"strings" |
|||
"testing" |
|||
) |
|||
|
|||
func TestGetTableLocationMappingEntryPathPerTable(t *testing.T) { |
|||
tableLocationBucket := "shared-location--table-s3" |
|||
tablePathA := GetTablePath("warehouse", "analytics", "orders") |
|||
tablePathB := GetTablePath("warehouse", "analytics", "customers") |
|||
|
|||
entryPathA := GetTableLocationMappingEntryPath(tableLocationBucket, tablePathA) |
|||
entryPathARepeat := GetTableLocationMappingEntryPath(tableLocationBucket, tablePathA) |
|||
entryPathB := GetTableLocationMappingEntryPath(tableLocationBucket, tablePathB) |
|||
|
|||
if entryPathA != entryPathARepeat { |
|||
t.Fatalf("mapping entry path should be deterministic: %q != %q", entryPathA, entryPathARepeat) |
|||
} |
|||
if entryPathA == entryPathB { |
|||
t.Fatalf("mapping entry path should differ per table path: %q == %q", entryPathA, entryPathB) |
|||
} |
|||
|
|||
expectedPrefix := GetTableLocationMappingPath(tableLocationBucket) + "/" |
|||
if !strings.HasPrefix(entryPathA, expectedPrefix) { |
|||
t.Fatalf("mapping entry path %q should start with %q", entryPathA, expectedPrefix) |
|||
} |
|||
if strings.TrimPrefix(entryPathA, expectedPrefix) == "" { |
|||
t.Fatalf("mapping entry name should not be empty: %q", entryPathA) |
|||
} |
|||
} |
|||
|
|||
func TestTableBucketPathFromTablePath(t *testing.T) { |
|||
testCases := []struct { |
|||
name string |
|||
tablePath string |
|||
expected string |
|||
ok bool |
|||
}{ |
|||
{ |
|||
name: "valid table path", |
|||
tablePath: GetTablePath("warehouse", "analytics", "orders"), |
|||
expected: GetTableBucketPath("warehouse"), |
|||
ok: true, |
|||
}, |
|||
{ |
|||
name: "valid table bucket root", |
|||
tablePath: GetTableBucketPath("warehouse"), |
|||
expected: GetTableBucketPath("warehouse"), |
|||
ok: true, |
|||
}, |
|||
{ |
|||
name: "invalid non-tables path", |
|||
tablePath: "/tmp/warehouse/analytics/orders", |
|||
expected: "", |
|||
ok: false, |
|||
}, |
|||
{ |
|||
name: "invalid empty bucket segment", |
|||
tablePath: "/buckets/", |
|||
expected: "", |
|||
ok: false, |
|||
}, |
|||
} |
|||
|
|||
for _, tc := range testCases { |
|||
t.Run(tc.name, func(t *testing.T) { |
|||
actual, ok := tableBucketPathFromTablePath(tc.tablePath) |
|||
if ok != tc.ok { |
|||
t.Fatalf("tableBucketPathFromTablePath(%q) ok=%v, want %v", tc.tablePath, ok, tc.ok) |
|||
} |
|||
if actual != tc.expected { |
|||
t.Fatalf("tableBucketPathFromTablePath(%q)=%q, want %q", tc.tablePath, actual, tc.expected) |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue