package utils_test import ( "fmt" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "time" "github.com/aldinokemal/go-whatsapp-web-multidevice/config" "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) type UtilsTestSuite struct { suite.Suite } func (suite *UtilsTestSuite) TestContainsMention() { type args struct { message string } tests := []struct { name string args args want []string }{ { name: "should success get phone when @ with space", args: args{message: "welcome @6289123 ."}, want: []string{"6289123"}, }, { name: "should success get phone without suffix space", args: args{message: "welcome @6289123."}, want: []string{"6289123"}, }, { name: "should success get phone without prefix space", args: args{message: "welcome@6289123.@hello:@62891823"}, want: []string{"6289123", "62891823"}, }, } for _, tt := range tests { suite.T().Run(tt.name, func(t *testing.T) { got := utils.ContainsMention(tt.args.message) assert.Equal(t, tt.want, got) }) } } func (suite *UtilsTestSuite) TestRemoveFile() { tempFile, err := os.CreateTemp("", "testfile") assert.NoError(suite.T(), err) tempFilePath := tempFile.Name() tempFile.Close() err = utils.RemoveFile(0, tempFilePath) assert.NoError(suite.T(), err) _, err = os.Stat(tempFilePath) assert.True(suite.T(), os.IsNotExist(err)) } func (suite *UtilsTestSuite) TestCreateFolder() { tempDir := "testdir" err := utils.CreateFolder(tempDir) assert.NoError(suite.T(), err) _, err = os.Stat(tempDir) assert.NoError(suite.T(), err) assert.True(suite.T(), err == nil) os.RemoveAll(tempDir) } func (suite *UtilsTestSuite) TestPanicIfNeeded() { assert.PanicsWithValue(suite.T(), "test error", func() { utils.PanicIfNeeded("test error") }) assert.NotPanics(suite.T(), func() { utils.PanicIfNeeded(nil) }) } func (suite *UtilsTestSuite) TestStrToFloat64() { assert.Equal(suite.T(), 123.45, utils.StrToFloat64("123.45")) assert.Equal(suite.T(), 0.0, utils.StrToFloat64("invalid")) assert.Equal(suite.T(), 0.0, utils.StrToFloat64("")) } func (suite *UtilsTestSuite) TestGetMetaDataFromURL() { // Use httptest.NewServer to mock HTTP server server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(`Test Title`)) })) defer server.Close() // Ensure the server is closed when the test ends meta, err := utils.GetMetaDataFromURL(server.URL) assert.NoError(suite.T(), err) assert.Equal(suite.T(), "Test Title", meta.Title) assert.Equal(suite.T(), "Test Description", meta.Description) assert.Equal(suite.T(), "http://example.com/image.jpg", meta.Image) } func (suite *UtilsTestSuite) TestGetMetaDataFromURLEdgeCases() { // Test with OG title and Twitter image server1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(``)) })) defer server1.Close() meta, err := utils.GetMetaDataFromURL(server1.URL) assert.NoError(suite.T(), err) assert.Equal(suite.T(), "OG Title", meta.Title) assert.Contains(suite.T(), meta.Image, "relative-image.jpg") // Test with empty title falling back to title tag server2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(`Fallback Title`)) })) defer server2.Close() meta, err = utils.GetMetaDataFromURL(server2.URL) assert.NoError(suite.T(), err) assert.Equal(suite.T(), "Fallback Title", meta.Title) // Test invalid URL _, err = utils.GetMetaDataFromURL("not-a-valid-url") assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "unsupported protocol scheme") // Test HTTP error errorServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) })) defer errorServer.Close() _, err = utils.GetMetaDataFromURL(errorServer.URL) assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "HTTP request failed") // Test malformed HTML malformedServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(`Test`)) })) defer malformedServer.Close() meta, err = utils.GetMetaDataFromURL(malformedServer.URL) assert.NoError(suite.T(), err) // Should handle malformed HTML gracefully assert.Equal(suite.T(), "Test", meta.Title) // Test timeout with slow server slowServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { time.Sleep(20 * time.Second) // Longer than client timeout w.Write([]byte("slow response")) })) defer slowServer.Close() _, err = utils.GetMetaDataFromURL(slowServer.URL) assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "deadline exceeded") // Test too many redirects redirectServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, r.URL.String(), http.StatusFound) })) defer redirectServer.Close() _, err = utils.GetMetaDataFromURL(redirectServer.URL) assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "too many redirects") // Test with image that has content type but invalid image data (tests error handling path) var imageServerURL string imageServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/invalid.jpg" { // Serve content with image content type but invalid image data w.Header().Set("Content-Type", "image/jpeg") w.Write([]byte("invalid image data")) } else { w.Write([]byte(`Image Test`)) } })) imageServerURL = imageServer.URL defer imageServer.Close() meta, err = utils.GetMetaDataFromURL(imageServer.URL) assert.NoError(suite.T(), err) // Should handle invalid image gracefully assert.Equal(suite.T(), "Image Test", meta.Title) assert.Contains(suite.T(), meta.Image, "/invalid.jpg") // Image download may fail but meta should still be extracted } func (suite *UtilsTestSuite) TestDownloadImageFromURL() { // Use httptest.NewServer to mock HTTP server server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/image.jpg" { w.Header().Set("Content-Type", "image/jpeg") // Set content type to image w.Write([]byte("image data")) } else { http.NotFound(w, r) } })) defer server.Close() // Ensure the server is closed when the test ends imageData, fileName, err := utils.DownloadImageFromURL(server.URL + "/image.jpg") assert.NoError(suite.T(), err) assert.Equal(suite.T(), []byte("image data"), imageData) assert.Equal(suite.T(), "image.jpg", fileName) } func (suite *UtilsTestSuite) TestRemoveFileEdgeCases() { // Test empty path handling err := utils.RemoveFile(0, "") assert.NoError(suite.T(), err, "Should handle empty path gracefully") // Test multiple files removal tempFile1, err := os.CreateTemp("", "testfile1") assert.NoError(suite.T(), err) tempFile1Path := tempFile1.Name() tempFile1.Close() tempFile2, err := os.CreateTemp("", "testfile2") assert.NoError(suite.T(), err) tempFile2Path := tempFile2.Name() tempFile2.Close() err = utils.RemoveFile(0, tempFile1Path, tempFile2Path) assert.NoError(suite.T(), err) // Verify both files are removed _, err = os.Stat(tempFile1Path) assert.True(suite.T(), os.IsNotExist(err)) _, err = os.Stat(tempFile2Path) assert.True(suite.T(), os.IsNotExist(err)) // Test delay functionality tempFile3, err := os.CreateTemp("", "testfile3") assert.NoError(suite.T(), err) tempFile3Path := tempFile3.Name() tempFile3.Close() start := time.Now() err = utils.RemoveFile(1, tempFile3Path) // 1 second delay duration := time.Since(start) assert.NoError(suite.T(), err) assert.True(suite.T(), duration >= time.Second, "Should respect delay parameter") // Test non-existent file error err = utils.RemoveFile(0, "/non/existent/file.txt") assert.Error(suite.T(), err, "Should return error for non-existent file") } func (suite *UtilsTestSuite) TestCreateFolderEdgeCases() { // Test nested folder creation nestedPath := filepath.Join("test", "nested", "folder") err := utils.CreateFolder(nestedPath) assert.NoError(suite.T(), err) _, err = os.Stat(nestedPath) assert.NoError(suite.T(), err) os.RemoveAll("test") // Test multiple folders creation folder1 := "testfolder1" folder2 := "testfolder2" err = utils.CreateFolder(folder1, folder2) assert.NoError(suite.T(), err) _, err = os.Stat(folder1) assert.NoError(suite.T(), err) _, err = os.Stat(folder2) assert.NoError(suite.T(), err) os.RemoveAll(folder1) os.RemoveAll(folder2) // Test creating folder that already exists existingFolder := "existing" err = os.Mkdir(existingFolder, 0755) assert.NoError(suite.T(), err) err = utils.CreateFolder(existingFolder) assert.NoError(suite.T(), err, "Should handle existing folder gracefully") os.RemoveAll(existingFolder) } func (suite *UtilsTestSuite) TestPanicIfNeededEdgeCases() { // Test "record not found" with custom message assert.PanicsWithValue(suite.T(), "Custom not found message", func() { utils.PanicIfNeeded("record not found", "Custom not found message") }) // Test "record not found" without custom message assert.PanicsWithValue(suite.T(), "record not found", func() { utils.PanicIfNeeded("record not found") }) // Test other error types assert.PanicsWithValue(suite.T(), "some other error", func() { utils.PanicIfNeeded("some other error") }) // Test with error interface testErr := fmt.Errorf("test error") assert.PanicsWithValue(suite.T(), testErr, func() { utils.PanicIfNeeded(testErr) }) } func (suite *UtilsTestSuite) TestStrToFloat64EdgeCases() { // Test with whitespace assert.Equal(suite.T(), 123.45, utils.StrToFloat64(" 123.45 ")) // Test with negative numbers assert.Equal(suite.T(), -123.45, utils.StrToFloat64("-123.45")) // Test with zero assert.Equal(suite.T(), 0.0, utils.StrToFloat64("0")) assert.Equal(suite.T(), 0.0, utils.StrToFloat64("0.0")) // Test with scientific notation assert.Equal(suite.T(), 1.23e2, utils.StrToFloat64("1.23e2")) // Test with various invalid inputs assert.Equal(suite.T(), 0.0, utils.StrToFloat64("not_a_number")) assert.Equal(suite.T(), 0.0, utils.StrToFloat64("123.45.67")) assert.Equal(suite.T(), 0.0, utils.StrToFloat64("abc123")) } func (suite *UtilsTestSuite) TestDownloadImageFromURLEdgeCases() { // Test non-image content type server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") w.Write([]byte("not an image")) })) defer server.Close() _, _, err := utils.DownloadImageFromURL(server.URL) assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "invalid content type") // Test HTTP error status errorServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) })) defer errorServer.Close() _, _, err = utils.DownloadImageFromURL(errorServer.URL) assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "HTTP request failed") // Test unsupported file extension extServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/image.gif" { w.Header().Set("Content-Type", "image/gif") w.Write([]byte("gif data")) } })) defer extServer.Close() _, _, err = utils.DownloadImageFromURL(extServer.URL + "/image.gif") assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "unsupported file type") // Test valid image extensions validExtServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path if strings.HasSuffix(path, ".jpg") { w.Header().Set("Content-Type", "image/jpeg") } else if strings.HasSuffix(path, ".png") { w.Header().Set("Content-Type", "image/png") } else if strings.HasSuffix(path, ".webp") { w.Header().Set("Content-Type", "image/webp") } w.Write([]byte("valid image data")) })) defer validExtServer.Close() // Test .jpg data, filename, err := utils.DownloadImageFromURL(validExtServer.URL + "/test.jpg") assert.NoError(suite.T(), err) assert.Equal(suite.T(), "test.jpg", filename) assert.Equal(suite.T(), []byte("valid image data"), data) // Test .png data, filename, err = utils.DownloadImageFromURL(validExtServer.URL + "/test.png") assert.NoError(suite.T(), err) assert.Equal(suite.T(), "test.png", filename) // Test .webp data, filename, err = utils.DownloadImageFromURL(validExtServer.URL + "/test.webp") assert.NoError(suite.T(), err) assert.Equal(suite.T(), "test.webp", filename) // Test filename extraction with query parameters data, filename, err = utils.DownloadImageFromURL(validExtServer.URL + "/test.jpg?v=1&size=large") assert.NoError(suite.T(), err) assert.Equal(suite.T(), "test.jpg", filename) } func (suite *UtilsTestSuite) TestDownloadAudioFromURL() { // Mock original config values origMaxSize := config.WhatsappSettingMaxDownloadSize config.WhatsappSettingMaxDownloadSize = 1024 * 1024 // 1MB for testing defer func() { config.WhatsappSettingMaxDownloadSize = origMaxSize }() // Test successful audio download server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path if path == "/test.mp3" { w.Header().Set("Content-Type", "audio/mpeg") w.Write([]byte("audio data")) } else if path == "/test.wav" { w.Header().Set("Content-Type", "audio/wav") w.Write([]byte("wav audio data")) } else if path == "/test.ogg" { w.Header().Set("Content-Type", "audio/ogg") w.Write([]byte("ogg audio data")) } else if path == "/test.m4a" { w.Header().Set("Content-Type", "audio/m4a") w.Write([]byte("m4a audio data")) } else if path == "/large.mp3" { w.Header().Set("Content-Type", "audio/mpeg") w.Header().Set("Content-Length", "2097152") // 2MB w.Write([]byte("large audio")) } else if path == "/invalid.mp3" { w.Header().Set("Content-Type", "text/html") w.Write([]byte("not audio")) } else if path == "/no-filename/" { w.Header().Set("Content-Type", "audio/mpeg") w.Write([]byte("audio without filename")) } })) defer server.Close() // Test valid MP3 download data, filename, err := utils.DownloadAudioFromURL(server.URL + "/test.mp3") assert.NoError(suite.T(), err) assert.Equal(suite.T(), "test.mp3", filename) assert.Equal(suite.T(), []byte("audio data"), data) // Test valid WAV download data, filename, err = utils.DownloadAudioFromURL(server.URL + "/test.wav") assert.NoError(suite.T(), err) assert.Equal(suite.T(), "test.wav", filename) // Test valid OGG download data, filename, err = utils.DownloadAudioFromURL(server.URL + "/test.ogg") assert.NoError(suite.T(), err) assert.Equal(suite.T(), "test.ogg", filename) // Test valid M4A download data, filename, err = utils.DownloadAudioFromURL(server.URL + "/test.m4a") assert.NoError(suite.T(), err) assert.Equal(suite.T(), "test.m4a", filename) // Test invalid content type _, _, err = utils.DownloadAudioFromURL(server.URL + "/invalid.mp3") assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "invalid content type") // Test file too large by content length _, _, err = utils.DownloadAudioFromURL(server.URL + "/large.mp3") assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "exceeds maximum allowed size") // Test HTTP error errorServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) })) defer errorServer.Close() _, _, err = utils.DownloadAudioFromURL(errorServer.URL + "/error.mp3") assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "HTTP request failed") // Test filename without extension (should generate timestamp-based name) data, filename, err = utils.DownloadAudioFromURL(server.URL + "/no-filename/") assert.NoError(suite.T(), err) assert.Contains(suite.T(), filename, "audio_") assert.Equal(suite.T(), []byte("audio without filename"), data) // Test URL with query parameters data, filename, err = utils.DownloadAudioFromURL(server.URL + "/test.mp3?v=1&quality=high") assert.NoError(suite.T(), err) assert.Equal(suite.T(), "test.mp3", filename) // Test invalid URL _, _, err = utils.DownloadAudioFromURL("not-a-valid-url") assert.Error(suite.T(), err) // Test too many redirects redirectServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, r.URL.String(), http.StatusFound) })) defer redirectServer.Close() _, _, err = utils.DownloadAudioFromURL(redirectServer.URL + "/redirect.mp3") assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "too many redirects") } func (suite *UtilsTestSuite) TestDownloadVideoFromURL() { // Mock original config values origMaxSize := config.WhatsappSettingMaxDownloadSize config.WhatsappSettingMaxDownloadSize = 1024 * 1024 // 1MB for testing defer func() { config.WhatsappSettingMaxDownloadSize = origMaxSize }() // Test successful video download server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path if path == "/test.mp4" { w.Header().Set("Content-Type", "video/mp4") w.Write([]byte("video data")) } else if path == "/test.mkv" { w.Header().Set("Content-Type", "video/x-matroska") w.Write([]byte("mkv video data")) } else if path == "/test.avi" { w.Header().Set("Content-Type", "video/avi") w.Write([]byte("avi video data")) } else if path == "/large.mp4" { w.Header().Set("Content-Type", "video/mp4") w.Header().Set("Content-Length", "2097152") // 2MB w.Write([]byte("large video")) } else if path == "/invalid.mp4" { w.Header().Set("Content-Type", "text/html") w.Write([]byte("not video")) } else if path == "/no-filename/" { w.Header().Set("Content-Type", "video/mp4") w.Write([]byte("video without filename")) } })) defer server.Close() // Test valid MP4 download data, filename, err := utils.DownloadVideoFromURL(server.URL + "/test.mp4") assert.NoError(suite.T(), err) assert.Equal(suite.T(), "test.mp4", filename) assert.Equal(suite.T(), []byte("video data"), data) // Test valid MKV download data, filename, err = utils.DownloadVideoFromURL(server.URL + "/test.mkv") assert.NoError(suite.T(), err) assert.Equal(suite.T(), "test.mkv", filename) // Test valid AVI download data, filename, err = utils.DownloadVideoFromURL(server.URL + "/test.avi") assert.NoError(suite.T(), err) assert.Equal(suite.T(), "test.avi", filename) // Test invalid content type _, _, err = utils.DownloadVideoFromURL(server.URL + "/invalid.mp4") assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "invalid content type") // Test file too large by content length _, _, err = utils.DownloadVideoFromURL(server.URL + "/large.mp4") assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "exceeds maximum allowed size") // Test HTTP error errorServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) })) defer errorServer.Close() _, _, err = utils.DownloadVideoFromURL(errorServer.URL + "/error.mp4") assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "HTTP request failed") // Test filename without extension (should generate timestamp-based name) data, filename, err = utils.DownloadVideoFromURL(server.URL + "/no-filename/") assert.NoError(suite.T(), err) assert.Contains(suite.T(), filename, "video_") assert.True(suite.T(), strings.HasSuffix(filename, ".mp4")) assert.Equal(suite.T(), []byte("video without filename"), data) // Test URL with query parameters data, filename, err = utils.DownloadVideoFromURL(server.URL + "/test.mp4?v=1&quality=hd") assert.NoError(suite.T(), err) assert.Equal(suite.T(), "test.mp4", filename) // Test invalid URL _, _, err = utils.DownloadVideoFromURL("not-a-valid-url") assert.Error(suite.T(), err) // Test too many redirects redirectServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, r.URL.String(), http.StatusFound) })) defer redirectServer.Close() _, _, err = utils.DownloadVideoFromURL(redirectServer.URL + "/redirect.mp4") assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "too many redirects") } func TestUtilsTestSuite(t *testing.T) { suite.Run(t, new(UtilsTestSuite)) }