| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- package azuresink
- import (
- "os"
- "testing"
- "time"
- "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
- )
- // MockConfiguration for testing
- type mockConfiguration struct {
- values map[string]interface{}
- }
- func newMockConfiguration() *mockConfiguration {
- return &mockConfiguration{
- values: make(map[string]interface{}),
- }
- }
- func (m *mockConfiguration) GetString(key string) string {
- if v, ok := m.values[key]; ok {
- return v.(string)
- }
- return ""
- }
- func (m *mockConfiguration) GetBool(key string) bool {
- if v, ok := m.values[key]; ok {
- return v.(bool)
- }
- return false
- }
- func (m *mockConfiguration) GetInt(key string) int {
- if v, ok := m.values[key]; ok {
- return v.(int)
- }
- return 0
- }
- func (m *mockConfiguration) GetInt64(key string) int64 {
- if v, ok := m.values[key]; ok {
- return v.(int64)
- }
- return 0
- }
- func (m *mockConfiguration) GetFloat64(key string) float64 {
- if v, ok := m.values[key]; ok {
- return v.(float64)
- }
- return 0.0
- }
- func (m *mockConfiguration) GetStringSlice(key string) []string {
- if v, ok := m.values[key]; ok {
- return v.([]string)
- }
- return nil
- }
- func (m *mockConfiguration) SetDefault(key string, value interface{}) {
- if _, exists := m.values[key]; !exists {
- m.values[key] = value
- }
- }
- // Test the AzureSink interface implementation
- func TestAzureSinkInterface(t *testing.T) {
- sink := &AzureSink{}
- if sink.GetName() != "azure" {
- t.Errorf("Expected name 'azure', got '%s'", sink.GetName())
- }
- // Test directory setting
- sink.dir = "/test/dir"
- if sink.GetSinkToDirectory() != "/test/dir" {
- t.Errorf("Expected directory '/test/dir', got '%s'", sink.GetSinkToDirectory())
- }
- // Test incremental setting
- sink.isIncremental = true
- if !sink.IsIncremental() {
- t.Error("Expected isIncremental to be true")
- }
- }
- // Test Azure sink initialization
- func TestAzureSinkInitialization(t *testing.T) {
- accountName := os.Getenv("AZURE_STORAGE_ACCOUNT")
- accountKey := os.Getenv("AZURE_STORAGE_ACCESS_KEY")
- testContainer := os.Getenv("AZURE_TEST_CONTAINER")
- if accountName == "" || accountKey == "" {
- t.Skip("Skipping Azure sink test: AZURE_STORAGE_ACCOUNT or AZURE_STORAGE_ACCESS_KEY not set")
- }
- if testContainer == "" {
- testContainer = "seaweedfs-test"
- }
- sink := &AzureSink{}
- err := sink.initialize(accountName, accountKey, testContainer, "/test")
- if err != nil {
- t.Fatalf("Failed to initialize Azure sink: %v", err)
- }
- if sink.container != testContainer {
- t.Errorf("Expected container '%s', got '%s'", testContainer, sink.container)
- }
- if sink.dir != "/test" {
- t.Errorf("Expected dir '/test', got '%s'", sink.dir)
- }
- if sink.client == nil {
- t.Error("Expected client to be initialized")
- }
- }
- // Test configuration-based initialization
- func TestAzureSinkInitializeFromConfig(t *testing.T) {
- accountName := os.Getenv("AZURE_STORAGE_ACCOUNT")
- accountKey := os.Getenv("AZURE_STORAGE_ACCESS_KEY")
- testContainer := os.Getenv("AZURE_TEST_CONTAINER")
- if accountName == "" || accountKey == "" {
- t.Skip("Skipping Azure sink config test: AZURE_STORAGE_ACCOUNT or AZURE_STORAGE_ACCESS_KEY not set")
- }
- if testContainer == "" {
- testContainer = "seaweedfs-test"
- }
- config := newMockConfiguration()
- config.values["azure.account_name"] = accountName
- config.values["azure.account_key"] = accountKey
- config.values["azure.container"] = testContainer
- config.values["azure.directory"] = "/test"
- config.values["azure.is_incremental"] = true
- sink := &AzureSink{}
- err := sink.Initialize(config, "azure.")
- if err != nil {
- t.Fatalf("Failed to initialize from config: %v", err)
- }
- if !sink.IsIncremental() {
- t.Error("Expected incremental to be true")
- }
- }
- // Test cleanKey function
- func TestCleanKey(t *testing.T) {
- tests := []struct {
- input string
- expected string
- }{
- {"/test/file.txt", "test/file.txt"},
- {"test/file.txt", "test/file.txt"},
- {"/", ""},
- {"", ""},
- {"/a/b/c", "a/b/c"},
- }
- for _, tt := range tests {
- t.Run(tt.input, func(t *testing.T) {
- result := cleanKey(tt.input)
- if result != tt.expected {
- t.Errorf("cleanKey(%q) = %q, want %q", tt.input, result, tt.expected)
- }
- })
- }
- }
- // Test entry operations (requires valid credentials)
- func TestAzureSinkEntryOperations(t *testing.T) {
- accountName := os.Getenv("AZURE_STORAGE_ACCOUNT")
- accountKey := os.Getenv("AZURE_STORAGE_ACCESS_KEY")
- testContainer := os.Getenv("AZURE_TEST_CONTAINER")
- if accountName == "" || accountKey == "" {
- t.Skip("Skipping Azure sink entry test: credentials not set")
- }
- if testContainer == "" {
- testContainer = "seaweedfs-test"
- }
- sink := &AzureSink{}
- err := sink.initialize(accountName, accountKey, testContainer, "/test")
- if err != nil {
- t.Fatalf("Failed to initialize: %v", err)
- }
- // Test CreateEntry with directory (should be no-op)
- t.Run("CreateDirectory", func(t *testing.T) {
- entry := &filer_pb.Entry{
- IsDirectory: true,
- }
- err := sink.CreateEntry("/test/dir", entry, nil)
- if err != nil {
- t.Errorf("CreateEntry for directory should not error: %v", err)
- }
- })
- // Test CreateEntry with file
- testKey := "/test-sink-file-" + time.Now().Format("20060102-150405") + ".txt"
- t.Run("CreateFile", func(t *testing.T) {
- entry := &filer_pb.Entry{
- IsDirectory: false,
- Content: []byte("Test content for Azure sink"),
- Attributes: &filer_pb.FuseAttributes{
- Mtime: time.Now().Unix(),
- },
- }
- err := sink.CreateEntry(testKey, entry, nil)
- if err != nil {
- t.Fatalf("Failed to create entry: %v", err)
- }
- })
- // Test UpdateEntry
- t.Run("UpdateEntry", func(t *testing.T) {
- oldEntry := &filer_pb.Entry{
- Content: []byte("Old content"),
- }
- newEntry := &filer_pb.Entry{
- Content: []byte("New content for update test"),
- Attributes: &filer_pb.FuseAttributes{
- Mtime: time.Now().Unix(),
- },
- }
- found, err := sink.UpdateEntry(testKey, oldEntry, "/test", newEntry, false, nil)
- if err != nil {
- t.Fatalf("Failed to update entry: %v", err)
- }
- if !found {
- t.Error("Expected found to be true")
- }
- })
- // Test DeleteEntry
- t.Run("DeleteFile", func(t *testing.T) {
- err := sink.DeleteEntry(testKey, false, false, nil)
- if err != nil {
- t.Fatalf("Failed to delete entry: %v", err)
- }
- })
- // Test DeleteEntry with directory marker
- testDirKey := "/test-dir-" + time.Now().Format("20060102-150405")
- t.Run("DeleteDirectory", func(t *testing.T) {
- // First create a directory marker
- entry := &filer_pb.Entry{
- IsDirectory: false,
- Content: []byte(""),
- }
- err := sink.CreateEntry(testDirKey+"/", entry, nil)
- if err != nil {
- t.Logf("Warning: Failed to create directory marker: %v", err)
- }
- // Then delete it
- err = sink.DeleteEntry(testDirKey, true, false, nil)
- if err != nil {
- t.Logf("Warning: Failed to delete directory: %v", err)
- }
- })
- }
- // Test CreateEntry with precondition (IfUnmodifiedSince)
- func TestAzureSinkPrecondition(t *testing.T) {
- accountName := os.Getenv("AZURE_STORAGE_ACCOUNT")
- accountKey := os.Getenv("AZURE_STORAGE_ACCESS_KEY")
- testContainer := os.Getenv("AZURE_TEST_CONTAINER")
- if accountName == "" || accountKey == "" {
- t.Skip("Skipping Azure sink precondition test: credentials not set")
- }
- if testContainer == "" {
- testContainer = "seaweedfs-test"
- }
- sink := &AzureSink{}
- err := sink.initialize(accountName, accountKey, testContainer, "/test")
- if err != nil {
- t.Fatalf("Failed to initialize: %v", err)
- }
- testKey := "/test-precondition-" + time.Now().Format("20060102-150405") + ".txt"
- // Create initial entry
- entry := &filer_pb.Entry{
- Content: []byte("Initial content"),
- Attributes: &filer_pb.FuseAttributes{
- Mtime: time.Now().Unix(),
- },
- }
- err = sink.CreateEntry(testKey, entry, nil)
- if err != nil {
- t.Fatalf("Failed to create initial entry: %v", err)
- }
- // Try to create again with old mtime (should be skipped due to precondition)
- oldEntry := &filer_pb.Entry{
- Content: []byte("Should not overwrite"),
- Attributes: &filer_pb.FuseAttributes{
- Mtime: time.Now().Add(-1 * time.Hour).Unix(), // Old timestamp
- },
- }
- err = sink.CreateEntry(testKey, oldEntry, nil)
- // Should either succeed (skip) or fail with precondition error
- if err != nil {
- t.Logf("Create with old mtime: %v (expected)", err)
- }
- // Clean up
- sink.DeleteEntry(testKey, false, false, nil)
- }
- // Benchmark tests
- func BenchmarkCleanKey(b *testing.B) {
- keys := []string{
- "/simple/path.txt",
- "no/leading/slash.txt",
- "/",
- "/complex/path/with/many/segments/file.txt",
- }
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- cleanKey(keys[i%len(keys)])
- }
- }
- // Test error handling with invalid credentials
- func TestAzureSinkInvalidCredentials(t *testing.T) {
- sink := &AzureSink{}
- err := sink.initialize("invalid-account", "aW52YWxpZGtleQ==", "test-container", "/test")
- if err != nil {
- t.Skip("Invalid credentials correctly rejected at initialization")
- }
- // If initialization succeeded, operations should fail
- entry := &filer_pb.Entry{
- Content: []byte("test"),
- }
- err = sink.CreateEntry("/test.txt", entry, nil)
- if err == nil {
- t.Log("Expected error with invalid credentials, but got none (might be cached)")
- }
- }
|