From 6508f2c59997f7f91b0ea6583071e72920dfbc2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Wed, 21 Jan 2026 22:40:34 +0100 Subject: [PATCH] Remove upload file --- auth/auth_test.go | 6 +- buckets/upload.go | 73 ++--------- buckets/upload_test.go | 283 ----------------------------------------- users/test_helpers.go | 17 --- 4 files changed, 17 insertions(+), 362 deletions(-) delete mode 100644 users/test_helpers.go diff --git a/auth/auth_test.go b/auth/auth_test.go index 413266f..abb4e3f 100644 --- a/auth/auth_test.go +++ b/auth/auth_test.go @@ -39,21 +39,21 @@ func TestRefreshToken(t *testing.T) { token: "invalid-token", mockStatusCode: http.StatusUnauthorized, expectError: true, - errorContains: "refresh token failed with status 401", + errorContains: "refresh token: error message (status 401)", }, { name: "server error - 500", token: "valid-token", mockStatusCode: http.StatusInternalServerError, expectError: true, - errorContains: "refresh token failed with status 500", + errorContains: "refresh token: error message (status 500)", }, { name: "forbidden - 403", token: "valid-token", mockStatusCode: http.StatusForbidden, expectError: true, - errorContains: "refresh token failed with status 403", + errorContains: "refresh token: error message (status 403)", }, { name: "missing newToken in response", diff --git a/buckets/upload.go b/buckets/upload.go index c9ba798..94b7260 100644 --- a/buckets/upload.go +++ b/buckets/upload.go @@ -11,7 +11,6 @@ import ( "hash" "io" "net/http" - "os" "path/filepath" "strings" "sync" @@ -90,46 +89,10 @@ func uploadEncryptedData(ctx context.Context, cfg *config.Config, encryptedReade return finishResp.ID, nil } -func UploadFile(ctx context.Context, cfg *config.Config, filePath, targetFolderUUID string, modTime time.Time) (*CreateMetaResponse, error) { - f, err := os.Open(filePath) - if err != nil { - return nil, fmt.Errorf("failed to read file %s: %w", filePath, err) - } - defer f.Close() - - fileInfo, err := f.Stat() - if err != nil { - return nil, fmt.Errorf("failed to stat file %s: %w", filePath, err) - } - plainSize := fileInfo.Size() - - // Setup encryption - encryptedReader, sha256Hasher, encIndex, err := encryptionSetup(f, cfg) - if err != nil { - return nil, err - } - - // Upload to network - fileID, err := uploadEncryptedData(ctx, cfg, encryptedReader, sha256Hasher, encIndex, plainSize) - if err != nil { - return nil, fmt.Errorf("failed to transfer file data: %w", err) - } - - // Create Drive file metadata - base := filepath.Base(filePath) - name := strings.TrimSuffix(base, filepath.Ext(base)) - ext := strings.TrimPrefix(filepath.Ext(base), ".") - meta, err := CreateMetaFile(ctx, cfg, name, cfg.Bucket, &fileID, "03-aes", targetFolderUUID, name, ext, plainSize, modTime) - if err != nil { - return nil, fmt.Errorf("failed to create file metadata: %w", err) - } - return meta, nil -} - // UploadFileStream uploads data from the provided io.Reader into Internxt, // encrypting it on the fly and creating the metadata file in the target folder. // It returns the CreateMetaResponse of the created file entry. -func UploadFileStream(ctx context.Context, cfg *config.Config, targetFolderUUID, fileName string, in io.Reader, plainSize int64, modTime time.Time) (*CreateMetaResponse, error) { +func UploadFileStream(ctx context.Context, cfg *config.Config, targetFolderUUID, fileName string, in io.Reader, plainSize int64, modTime time.Time) (*string, error) { var ph [32]byte if _, err := rand.Read(ph[:]); err != nil { return nil, fmt.Errorf("cannot generate random index: %w", err) @@ -215,19 +178,12 @@ func UploadFileStream(ctx context.Context, cfg *config.Config, targetFolderUUID, return nil, fmt.Errorf("failed to finish upload: %w", err) } - base := filepath.Base(fileName) - name := strings.TrimSuffix(base, filepath.Ext(base)) - ext := strings.TrimPrefix(filepath.Ext(base), ".") - meta, err := CreateMetaFile(ctx, cfg, name, cfg.Bucket, &finishResp.ID, "03-aes", targetFolderUUID, name, ext, plainSize, modTime) - if err != nil { - return nil, fmt.Errorf("failed to create file metadata: %w", err) - } - return meta, nil + return &finishResp.ID, nil } // UploadFileStreamMultipart uploads data from an io.Reader using multipart upload. // This is intended for large files (>100MB) and splits the file into multiple chunks -func UploadFileStreamMultipart(ctx context.Context, cfg *config.Config, targetFolderUUID, fileName string, in io.Reader, plainSize int64, modTime time.Time) (*CreateMetaResponse, error) { +func UploadFileStreamMultipart(ctx context.Context, cfg *config.Config, targetFolderUUID, fileName string, in io.Reader, plainSize int64, modTime time.Time) (*string, error) { state, err := newMultipartUploadState(cfg, plainSize) if err != nil { return nil, fmt.Errorf("failed to initialize multipart upload state: %w", err) @@ -243,15 +199,7 @@ func UploadFileStreamMultipart(ctx context.Context, cfg *config.Config, targetFo return nil, fmt.Errorf("failed to finish multipart upload: %w", err) } - base := filepath.Base(fileName) - name := strings.TrimSuffix(base, filepath.Ext(base)) - ext := strings.TrimPrefix(filepath.Ext(base), ".") - meta, err := CreateMetaFile(ctx, cfg, name, cfg.Bucket, &finishResp.ID, "03-aes", targetFolderUUID, name, ext, plainSize, modTime) - if err != nil { - return nil, fmt.Errorf("failed to create file metadata: %w", err) - } - - return meta, nil + return &finishResp.ID, nil } // UploadFileStreamAuto automatically chooses between single-part and multipart upload @@ -290,24 +238,31 @@ func UploadFileStreamAuto(ctx context.Context, cfg *config.Config, targetFolderU var capturedData *bytes.Buffer var capturedReader io.Reader = in + base := filepath.Base(fileName) + name := strings.TrimSuffix(base, filepath.Ext(base)) ext := strings.TrimPrefix(filepath.Ext(fileName), ".") if thumbnails.IsSupportedFormat(ext) && plainSize > 0 && plainSize <= config.MaxThumbnailSourceSize { capturedData = &bytes.Buffer{} capturedReader = io.TeeReader(in, capturedData) } - var meta *CreateMetaResponse + var fileId *string var err error if plainSize >= config.DefaultMultipartMinSize { - meta, err = UploadFileStreamMultipart(ctx, cfg, targetFolderUUID, fileName, capturedReader, plainSize, modTime) + fileId, err = UploadFileStreamMultipart(ctx, cfg, targetFolderUUID, fileName, capturedReader, plainSize, modTime) } else { - meta, err = UploadFileStream(ctx, cfg, targetFolderUUID, fileName, capturedReader, plainSize, modTime) + fileId, err = UploadFileStream(ctx, cfg, targetFolderUUID, fileName, capturedReader, plainSize, modTime) } if err != nil { return nil, err } + meta, err := CreateMetaFile(ctx, cfg, name, cfg.Bucket, fileId, "03-aes", targetFolderUUID, name, ext, plainSize, modTime) + if err != nil { + return nil, fmt.Errorf("failed to create file metadata: %w", err) + } + if capturedData != nil && capturedData.Len() > 0 { thumbnailWG.Add(1) go uploadThumbnailAsync(ctx, cfg, meta.UUID, ext, capturedData.Bytes()) diff --git a/buckets/upload_test.go b/buckets/upload_test.go index e61374e..2aba5e6 100644 --- a/buckets/upload_test.go +++ b/buckets/upload_test.go @@ -7,8 +7,6 @@ import ( "fmt" "io" "net/http" - "os" - "path/filepath" "strings" "testing" "time" @@ -23,195 +21,6 @@ func newMockMultiEndpointServer() *mockMultiEndpointServer { return NewMockMultiEndpointServer() } -// TestUploadFile tests the complete file upload workflow from a file path -func TestUploadFile(t *testing.T) { - // Create a temporary test file - tmpDir := t.TempDir() - testFilePath := filepath.Join(tmpDir, "test-file.txt") - testContent := []byte("Hello, world! This is test content.") - if err := os.WriteFile(testFilePath, testContent, 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - testCases := []struct { - name string - filePath string - setupMock func(*mockMultiEndpointServer) - expectError bool - errorContains string - setupConfig func(*config.Config) - }{ - { - name: "successful upload", - filePath: testFilePath, - setupMock: func(m *mockMultiEndpointServer) { - // StartUpload handler - m.startHandler = func(w http.ResponseWriter, r *http.Request) { - resp := StartUploadResp{ - Uploads: []UploadPart{ - { - UUID: "part-uuid-123", - URL: m.URL() + "/upload/test-url", - URLs: []string{m.URL() + "/upload/test-url"}, - }, - }, - } - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(resp) - } - - // Transfer handler - m.transferHandler = func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("ETag", "\"test-etag-123\"") - w.WriteHeader(http.StatusOK) - } - - // FinishUpload handler - m.finishHandler = func(w http.ResponseWriter, r *http.Request) { - resp := FinishUploadResp{ - ID: TestFileID, - Bucket: TestBucket1, - Index: "test-index", - Created: "2025-01-01T00:00:00Z", - } - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(resp) - } - - // CreateMetaFile handler - m.createMetaHandler = func(w http.ResponseWriter, r *http.Request) { - resp := CreateMetaResponse{ - UUID: TestFileUUID, - FileID: TestFileID, - Name: "test-file", - Type: "txt", - } - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(resp) - } - }, - expectError: false, - }, - { - name: "error - file not found", - filePath: filepath.Join(tmpDir, "nonexistent.txt"), - setupMock: func(m *mockMultiEndpointServer) {}, - expectError: true, - errorContains: "failed to read file", - }, - { - name: "error - StartUpload fails", - filePath: testFilePath, - setupMock: func(m *mockMultiEndpointServer) { - m.startHandler = func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte("start upload error")) - } - }, - expectError: true, - errorContains: "failed to start upload", - }, - { - name: "error - Transfer fails", - filePath: testFilePath, - setupMock: func(m *mockMultiEndpointServer) { - m.startHandler = func(w http.ResponseWriter, r *http.Request) { - resp := StartUploadResp{ - Uploads: []UploadPart{ - { - UUID: "part-uuid", - URL: m.URL() + "/upload/test", - }, - }, - } - json.NewEncoder(w).Encode(resp) - } - m.transferHandler = func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusInternalServerError) - } - }, - expectError: true, - errorContains: "failed to transfer file data", - }, - { - name: "error - FinishUpload fails", - filePath: testFilePath, - setupMock: func(m *mockMultiEndpointServer) { - m.startHandler = func(w http.ResponseWriter, r *http.Request) { - resp := StartUploadResp{ - Uploads: []UploadPart{{UUID: "part-uuid", URL: m.URL() + "/upload/test"}}, - } - json.NewEncoder(w).Encode(resp) - } - m.transferHandler = func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("ETag", "\"etag\"") - w.WriteHeader(http.StatusOK) - } - m.finishHandler = func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("finish error")) - } - }, - expectError: true, - errorContains: "failed to finish upload", - }, - { - name: "error - CreateMetaFile fails", - filePath: testFilePath, - setupMock: func(m *mockMultiEndpointServer) { - m.startHandler = func(w http.ResponseWriter, r *http.Request) { - resp := StartUploadResp{ - Uploads: []UploadPart{{UUID: "part-uuid", URL: m.URL() + "/upload/test"}}, - } - json.NewEncoder(w).Encode(resp) - } - m.transferHandler = func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("ETag", "\"etag\"") - w.WriteHeader(http.StatusOK) - } - m.finishHandler = func(w http.ResponseWriter, r *http.Request) { - resp := FinishUploadResp{ID: "file-id"} - json.NewEncoder(w).Encode(resp) - } - m.createMetaHandler = func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusForbidden) - w.Write([]byte("forbidden")) - } - }, - expectError: true, - errorContains: "failed to create file metadata", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - mockServer := newMockMultiEndpointServer() - defer mockServer.Close() - - tc.setupMock(mockServer) - - cfg := newTestConfigWithSetup(mockServer.URL(), tc.setupConfig) - - result, err := UploadFile(context.Background(), cfg, tc.filePath, "folder-uuid-123", time.Now()) - - if tc.expectError { - if err == nil { - t.Error("expected error, got nil") - } else if tc.errorContains != "" && !strings.Contains(err.Error(), tc.errorContains) { - t.Errorf("expected error to contain '%s', got: %v", tc.errorContains, err) - } - } else { - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if result == nil { - t.Error("expected result, got nil") - } - } - }) - } -} - // TestUploadFileStream tests uploading from an io.Reader func TestUploadFileStream(t *testing.T) { testContent := []byte("Streaming upload test content") @@ -245,10 +54,6 @@ func TestUploadFileStream(t *testing.T) { resp := FinishUploadResp{ID: "stream-file-id"} json.NewEncoder(w).Encode(resp) } - m.createMetaHandler = func(w http.ResponseWriter, r *http.Request) { - resp := CreateMetaResponse{UUID: "stream-uuid", FileID: "stream-file-id"} - json.NewEncoder(w).Encode(resp) - } }, expectError: false, }, @@ -370,10 +175,6 @@ func TestUploadFileStreamMultipart(t *testing.T) { resp := FinishUploadResp{ID: "multipart-file-id"} json.NewEncoder(w).Encode(resp) } - m.createMetaHandler = func(w http.ResponseWriter, r *http.Request) { - resp := CreateMetaResponse{UUID: "multipart-uuid", FileID: "multipart-file-id"} - json.NewEncoder(w).Encode(resp) - } }, expectError: false, }, @@ -553,27 +354,6 @@ func TestUploadFileStreamAuto(t *testing.T) { } } -// TestUploadFileInvalidMnemonic tests that invalid mnemonic still generates keys -// (BIP39 doesn't validate mnemonic strength, just uses it as entropy) -func TestUploadFileInvalidMnemonic(t *testing.T) { - tmpDir := t.TempDir() - testFilePath := filepath.Join(tmpDir, "test.txt") - os.WriteFile(testFilePath, []byte("test"), 0644) - - cfg := newTestConfigWithSetup("http://localhost", func(c *config.Config) { - c.Mnemonic = "invalid mnemonic phrase that is not standard" - c.Bucket = TestBucket5 - }) - - _, err := UploadFile(context.Background(), cfg, testFilePath, TestFolderUUID, time.Now()) - if err == nil { - t.Error("expected error due to invalid mnemonic, got nil") - } - if !strings.Contains(err.Error(), "invalid mnemonic") { - t.Errorf("expected invalid mnemonic error, got: %v", err) - } -} - // TestUploadFileStreamContextCancellation tests context cancellation handling func TestUploadFileStreamContextCancellation(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) @@ -592,69 +372,6 @@ func TestUploadFileStreamContextCancellation(t *testing.T) { } } -// TestUploadFileNameParsing tests file name and extension parsing -func TestUploadFileNameParsing(t *testing.T) { - tmpDir := t.TempDir() - - testCases := []struct { - fileName string - expectedName string - expectedExt string - }{ - {"simple.txt", "simple", "txt"}, - {"multiple.dots.tar.gz", "multiple.dots.tar", "gz"}, - {"noextension", "noextension", ""}, - {".hidden", "", "hidden"}, - } - - for _, tc := range testCases { - t.Run(tc.fileName, func(t *testing.T) { - testFilePath := filepath.Join(tmpDir, tc.fileName) - os.WriteFile(testFilePath, []byte("content"), 0644) - - mockServer := newMockMultiEndpointServer() - defer mockServer.Close() - - var capturedName, capturedType string - mockServer.startHandler = func(w http.ResponseWriter, r *http.Request) { - json.NewEncoder(w).Encode(StartUploadResp{ - Uploads: []UploadPart{{UUID: "uuid", URL: mockServer.URL() + "/upload"}}, - }) - } - mockServer.transferHandler = func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("ETag", "\"etag\"") - w.WriteHeader(http.StatusOK) - } - mockServer.finishHandler = func(w http.ResponseWriter, r *http.Request) { - json.NewEncoder(w).Encode(FinishUploadResp{ID: TestFileID}) - } - mockServer.createMetaHandler = func(w http.ResponseWriter, r *http.Request) { - var req CreateMetaRequest - json.NewDecoder(r.Body).Decode(&req) - capturedName = req.PlainName - capturedType = req.Type - json.NewEncoder(w).Encode(CreateMetaResponse{UUID: "uuid", FileID: TestFileID}) - } - - cfg := newTestConfigWithSetup(mockServer.URL(), func(c *config.Config) { - c.Bucket = TestBucket7 - }) - - _, err := UploadFile(context.Background(), cfg, testFilePath, TestFolderUUID, time.Now()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - if capturedName != tc.expectedName { - t.Errorf("expected name '%s', got '%s'", tc.expectedName, capturedName) - } - if capturedType != tc.expectedExt { - t.Errorf("expected type '%s', got '%s'", tc.expectedExt, capturedType) - } - }) - } -} - // TestEncryptionSetup tests the encryptionSetup helper function func TestEncryptionSetup(t *testing.T) { testContent := []byte("test content for encryption") diff --git a/users/test_helpers.go b/users/test_helpers.go deleted file mode 100644 index f8abdc7..0000000 --- a/users/test_helpers.go +++ /dev/null @@ -1,17 +0,0 @@ -package users - -import ( - "github.com/internxt/rclone-adapter/config" - "github.com/internxt/rclone-adapter/endpoints" -) - -// newTestConfig creates a test config with the given mock server URL. -// The HTTPClient is properly configured with the centralized header transport. -func newTestConfig(mockServerURL string) *config.Config { - cfg := &config.Config{ - Token: "test-token", - Endpoints: endpoints.NewConfig(mockServerURL), - } - cfg.ApplyDefaults() - return cfg -}