package formatter import ( "bytes" "os" "testing" "git.db.org.ai/dborg/internal/models" ) func TestFormatBucketsResults(t *testing.T) { tests := []struct { name string response *models.BucketsSearchResponse asJSON bool wantErr bool }{ { name: "format_buckets_with_multiple_types", response: &models.BucketsSearchResponse{ Credits: models.CreditsInfo{ Unlimited: true, }, Results: map[string]any{ "buckets": []any{ map[string]any{ "bucket": "test-bucket.s3.amazonaws.com", "fileCount": float64(1500), "id": float64(1), "type": "aws", }, map[string]any{ "bucket": "another.s3-eu-west-1.amazonaws.com", "fileCount": float64(250), "id": float64(2), "type": "aws", }, map[string]any{ "bucket": "storage.googleapis.com", "fileCount": float64(10000), "id": float64(3), "type": "gcp", }, map[string]any{ "bucket": "spaces.digitaloceanspaces.com", "fileCount": float64(0), "id": float64(4), "type": "dos", }, }, }, }, asJSON: false, wantErr: false, }, { name: "format_empty_buckets", response: &models.BucketsSearchResponse{ Credits: models.CreditsInfo{ Remaining: 100, Unlimited: false, }, Results: nil, }, asJSON: false, wantErr: false, }, { name: "format_json_output", response: &models.BucketsSearchResponse{ Credits: models.CreditsInfo{ Unlimited: true, }, Results: map[string]any{ "buckets": []any{ map[string]any{ "bucket": "test.s3.amazonaws.com", "type": "aws", }, }, }, }, asJSON: true, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { oldStdout := os.Stdout r, w, _ := os.Pipe() os.Stdout = w err := FormatBucketsResults(tt.response, tt.asJSON) w.Close() var buf bytes.Buffer buf.ReadFrom(r) os.Stdout = oldStdout if (err != nil) != tt.wantErr { t.Errorf("FormatBucketsResults() error = %v, wantErr %v", err, tt.wantErr) return } output := buf.String() if !tt.asJSON && tt.response.Results != nil { if output == "" { t.Error("Expected non-empty output for non-JSON formatting") } } }) } } func TestFormatBucketFilesResults(t *testing.T) { tests := []struct { name string response *models.BucketsFilesSearchResponse asJSON bool wantErr bool }{ { name: "format_files_grouped_by_bucket", response: &models.BucketsFilesSearchResponse{ Credits: models.CreditsInfo{ Unlimited: true, }, Results: map[string]any{ "files": []any{ map[string]any{ "bucket": "test-bucket.s3.amazonaws.com", "bucketId": float64(123), "filename": "report.pdf", "fullPath": "documents/report.pdf", "file": "documents/report.pdf", "url": "https://test-bucket.s3.amazonaws.com/documents/report.pdf", "size": float64(1024000), "lastModified": float64(1704067200), "type": "aws", "id": "12345", }, map[string]any{ "bucket": "test-bucket.s3.amazonaws.com", "bucketId": float64(123), "filename": "logo.png", "fullPath": "images/logo.png", "file": "images/logo.png", "url": "https://test-bucket.s3.amazonaws.com/images/logo.png", "size": float64(50000), "lastModified": float64(1577836800), "type": "aws", }, map[string]any{ "bucket": "storage.blob.core.windows.net", "bucketId": float64(456), "filename": "data.json", "file": "data.json", "url": "https://storage.blob.core.windows.net/container/data.json", "type": "azure", "container": "container", }, }, }, }, asJSON: false, wantErr: false, }, { name: "format_empty_files", response: &models.BucketsFilesSearchResponse{ Credits: models.CreditsInfo{ Remaining: 50, Unlimited: false, }, Results: map[string]any{ "files": []any{}, }, }, asJSON: false, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { oldStdout := os.Stdout r, w, _ := os.Pipe() os.Stdout = w err := FormatBucketFilesResults(tt.response, tt.asJSON) w.Close() var buf bytes.Buffer buf.ReadFrom(r) os.Stdout = oldStdout if (err != nil) != tt.wantErr { t.Errorf("FormatBucketFilesResults() error = %v, wantErr %v", err, tt.wantErr) return } }) } } func TestGetBucketTypeHeader(t *testing.T) { tests := []struct { name string bucketType string wantNonEmpty bool }{ { name: "aws_header", bucketType: "aws", wantNonEmpty: true, }, { name: "gcp_header", bucketType: "gcp", wantNonEmpty: true, }, { name: "dos_header", bucketType: "dos", wantNonEmpty: true, }, { name: "unknown_header", bucketType: "custom", wantNonEmpty: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := getBucketTypeHeader(tt.bucketType) if tt.wantNonEmpty && got == "" { t.Errorf("getBucketTypeHeader() returned empty string for %s", tt.bucketType) } }) } } func TestFormatFileCount(t *testing.T) { tests := []struct { name string count int want bool }{ { name: "small_count", count: 10, want: true, }, { name: "medium_count", count: 500, want: true, }, { name: "large_count", count: 5000, want: true, }, { name: "very_large_count", count: 50000, want: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := formatFileCount(tt.count) if tt.want && got == "" { t.Errorf("formatFileCount() returned empty for count %d", tt.count) } }) } } func TestFormatNumber(t *testing.T) { tests := []struct { name string n int want string }{ { name: "small_number", n: 999, want: "999", }, { name: "thousand", n: 1000, want: "1,000", }, { name: "million", n: 1000000, want: "1,000,000", }, { name: "random_large", n: 12345678, want: "12,345,678", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := formatNumber(tt.n) if got != tt.want { t.Errorf("formatNumber() = %v, want %v", got, tt.want) } }) } } func TestGetExtensionColor(t *testing.T) { tests := []struct { name string ext string want string }{ { name: "pdf_extension", ext: "pdf", want: ColorBlue, }, { name: "image_extension", ext: "png", want: ColorGreen, }, { name: "video_extension", ext: "mp4", want: ColorMagenta, }, { name: "archive_extension", ext: "zip", want: ColorYellow, }, { name: "database_extension", ext: "sql", want: ColorRed, }, { name: "config_extension", ext: "json", want: ColorCyan, }, { name: "unknown_extension", ext: "xyz", want: ColorWhite, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := getExtensionColor(tt.ext) if got != tt.want { t.Errorf("getExtensionColor() = %v, want %v", got, tt.want) } }) } } func TestTruncateBucketName(t *testing.T) { tests := []struct { name string bucket string wantLen int }{ { name: "short_name", bucket: "test-bucket", wantLen: 50, }, { name: "long_aws_bucket", bucket: "very-long-bucket-name-that-exceeds-the-limit.s3.amazonaws.com", wantLen: 60, }, { name: "long_gcp_bucket", bucket: "extremely-long-google-cloud-storage-bucket-name.storage.googleapis.com", wantLen: 80, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := truncateBucketName(tt.bucket) if len(got) > tt.wantLen { t.Errorf("truncateBucketName() length = %d, want <= %d", len(got), tt.wantLen) } }) } } func TestExtractFileNameFromURL(t *testing.T) { tests := []struct { name string url string want string }{ { name: "simple_filename", url: "https://bucket.s3.amazonaws.com/file.pdf", want: "file.pdf", }, { name: "filename_with_path", url: "https://bucket.s3.amazonaws.com/path/to/document.docx", want: "document.docx", }, { name: "filename_with_query", url: "https://bucket.s3.amazonaws.com/image.jpg?version=123", want: "image.jpg", }, { name: "encoded_filename", url: "https://bucket.s3.amazonaws.com/my%20file%20name.pdf", want: "my%20file%20name.pdf", }, { name: "no_filename", url: "https://bucket.s3.amazonaws.com/", want: "index", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := extractFileNameFromURL(tt.url) if got != tt.want { t.Errorf("extractFileNameFromURL() = %v, want %v", got, tt.want) } }) } } func TestExtractBucketFromURL(t *testing.T) { tests := []struct { name string url string want string }{ { name: "aws_s3_url", url: "https://my-bucket.s3.amazonaws.com/file.pdf", want: "my-bucket.s3.amazonaws.com", }, { name: "digitalocean_spaces", url: "https://space.nyc3.digitaloceanspaces.com/path/file.jpg", want: "space.nyc3.digitaloceanspaces.com", }, { name: "gcp_storage", url: "https://bucket.storage.googleapis.com/data.json", want: "bucket.storage.googleapis.com", }, { name: "http_url", url: "http://bucket.example.com/file.txt", want: "bucket.example.com", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := extractBucketFromURL(tt.url) if got != tt.want { t.Errorf("extractBucketFromURL() = %v, want %v", got, tt.want) } }) } } func TestDecodeURLEncoding(t *testing.T) { tests := []struct { name string s string want string }{ { name: "spaces", s: "my%20file%20name.pdf", want: "my file name.pdf", }, { name: "parentheses", s: "document%28final%29.docx", want: "document(final).docx", }, { name: "underscores", s: "test_file_name.jpg", want: "test file name.jpg", }, { name: "mixed", s: "WhatsApp%20Image_2021%2810%29.jpeg", want: "WhatsApp Image 2021(10).jpeg", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := decodeURLEncoding(tt.s) if got != tt.want { t.Errorf("decodeURLEncoding() = %v, want %v", got, tt.want) } }) } }