diff --git a/cmd/info.go b/cmd/info.go index 3c73fa6..6ab7cb2 100644 --- a/cmd/info.go +++ b/cmd/info.go @@ -6,6 +6,7 @@ import ( "github.com/spf13/cobra" + "github.com/theredditbandit/pman/pkg/db" "github.com/theredditbandit/pman/pkg/utils" ) @@ -23,7 +24,7 @@ var infoCmd = &cobra.Command{ return ErrBadUsageInfoCmd } projectName := args[0] - infoData, err := utils.ReadREADME(projectName) + infoData, err := utils.ReadREADME(db.DBName, projectName) if err != nil { return err } diff --git a/pkg/db/db_test.go b/pkg/db/db_test.go index 11cbb73..3a6419e 100644 --- a/pkg/db/db_test.go +++ b/pkg/db/db_test.go @@ -11,9 +11,11 @@ import ( "github.com/theredditbandit/pman/pkg/db" ) -const dbname = db.DBTestName -const bucketName = "testBucket" -const key = "testKey" +const ( + dbname = db.DBTestName + bucketName = "testBucket" + key = "testKey" +) func Test_GetDBLoc(t *testing.T) { t.Run("Test getDBLoc", func(t *testing.T) { diff --git a/pkg/ui/interactiveTable.go b/pkg/ui/interactiveTable.go index 5e941d3..feb2eaa 100644 --- a/pkg/ui/interactiveTable.go +++ b/pkg/ui/interactiveTable.go @@ -66,7 +66,7 @@ func RenderInteractiveTable(data map[string]string) error { var rows []table.Row for proj, status := range data { alias, err := db.GetRecord(db.DBName, proj, pkg.ProjectAliasBucket) - lastEdited := utils.GetLastModifiedTime(proj) + lastEdited := utils.GetLastModifiedTime(db.DBName, proj) if err == nil { pname := fmt.Sprintf("%s (%s)", proj, alias) row := []string{utils.TitleCase(status), pname, lastEdited} // Status | projectName (alias) | lastEdited diff --git a/pkg/ui/pager/renderer.go b/pkg/ui/pager/renderer.go index 5b47f7b..950c60d 100644 --- a/pkg/ui/pager/renderer.go +++ b/pkg/ui/pager/renderer.go @@ -8,6 +8,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + "github.com/theredditbandit/pman/pkg/db" "github.com/theredditbandit/pman/pkg/utils" ) @@ -101,7 +102,7 @@ func (m model) footerView() string { } func LaunchRenderer(file string) error { - content, err := utils.ReadREADME(file) + content, err := utils.ReadREADME(db.DBName, file) if err != nil { return err } diff --git a/pkg/ui/statusTable.go b/pkg/ui/statusTable.go index fc3338f..73bcf9c 100644 --- a/pkg/ui/statusTable.go +++ b/pkg/ui/statusTable.go @@ -18,7 +18,7 @@ func RenderTable(data map[string]string) error { var tableData [][]string for p, status := range data { alias, err := db.GetRecord(db.DBName, p, pkg.ProjectAliasBucket) - lastEdited := utils.GetLastModifiedTime(p) + lastEdited := utils.GetLastModifiedTime(db.DBName, p) if err == nil { pname := fmt.Sprintf("%s (%s)", p, alias) row := []string{utils.TitleCase(status), pname, lastEdited} // Status | projectName (alias) | lastEdited diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 670afdf..fee6d4b 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -18,6 +18,7 @@ import ( var ( ErrBeautifyMD = errors.New("error beautifying markdown") + ErrGetProject = errors.New("error getting project") ErrReadREADME = errors.New("error reading README") ) @@ -36,40 +37,23 @@ func FilterByStatus(data map[string]string, status string) map[string]string { return filteredData } -// Deprecated: Use ui.RenderTable instead -func PrintData(data map[string]string) { - for k, v := range data { - alias, err := db.GetRecord(db.DBName, k, pkg.ProjectAliasBucket) - if err == nil { - log.Printf("%s : %s (%s) \n", TitleCase(v), k, alias) - } else { - log.Printf("%s : %s \n", TitleCase(v), k) - } - } -} - -func GetLastModifiedTime(pname string) string { +func GetLastModifiedTime(dbname, pname string) string { var lastModTime time.Time var lastModFile string today := time.Now() _ = lastModFile - pPath, err := db.GetRecord(db.DBName, pname, pkg.ProjectPaths) + pPath, err := db.GetRecord(dbname, pname, pkg.ProjectPaths) if err != nil { return "Something went wrong" } - err = filepath.Walk(pPath, func(_ string, info os.FileInfo, err error) error { - if err != nil { - return err - } + _ = filepath.Walk(pPath, func(_ string, info os.FileInfo, err error) error { if !info.IsDir() && info.ModTime().After(lastModTime) { lastModTime = info.ModTime() lastModFile = info.Name() } return nil }) - if err != nil { - return "Something went wrong" - } + switch fmt.Sprint(lastModTime.Date()) { case fmt.Sprint(today.Date()): return fmt.Sprintf("Today %s", lastModTime.Format("15:04")) @@ -95,25 +79,20 @@ func BeautifyMD(data []byte) (string, error) { } // ReadREADME: returns the byte array of README.md of a project -func ReadREADME(projectName string) ([]byte, error) { - path, err := db.GetRecord(db.DBName, projectName, pkg.ProjectPaths) - if err != nil { - actualName, err := db.GetRecord(db.DBName, projectName, pkg.ProjectAliasBucket) - if err != nil { - log.Printf("project: %v not a valid project\n", projectName) - return nil, errors.Join(ErrReadREADME, err) - } +func ReadREADME(dbname, projectName string) ([]byte, error) { + actualName, err := db.GetRecord(dbname, projectName, pkg.ProjectAliasBucket) + if err == nil { projectName = actualName - path, err = db.GetRecord(db.DBName, projectName, pkg.ProjectPaths) - if err != nil { - log.Printf("project: %v not a valid project\n", projectName) - return nil, errors.Join(ErrReadREADME, err) - } + } + path, err := db.GetRecord(dbname, projectName, pkg.ProjectPaths) + if err != nil { + log.Printf("project: %v not a valid project\n", projectName) + return nil, errors.Join(ErrGetProject, err) } pPath := filepath.Join(path, "README.md") data, err := os.ReadFile(pPath) if err != nil { - return nil, fmt.Errorf("something went wrong while reading README for %s: %w", projectName, err) + return nil, errors.Join(ErrReadREADME, err) } return data, nil } diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go new file mode 100644 index 0000000..9148f10 --- /dev/null +++ b/pkg/utils/utils_test.go @@ -0,0 +1,267 @@ +package utils_test + +import ( + "os" + "testing" + "time" + + "github.com/charmbracelet/glamour" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/theredditbandit/pman/pkg" + "github.com/theredditbandit/pman/pkg/db" + "github.com/theredditbandit/pman/pkg/utils" +) + +const ( + dbname = db.DBTestName + expectedMsg = "Something went wrong" + projectName = "project_name" + projectPath = "./" + projectName +) + +func Test_TitleCase(t *testing.T) { + t.Run("Test TitleCase", func(t *testing.T) { + s := "hello world" + expected := "Hello World" + + actual := utils.TitleCase(s) + + assert.Equal(t, expected, actual) + }) +} + +func Test_FilterByStatus(t *testing.T) { + t.Run("Test FilterByStatus", func(t *testing.T) { + data := map[string]string{ + "key1": "status1", + "key2": "status2", + "key3": "status1", + } + status := "status1" + + expectedData := map[string]string{ + "key1": "status1", + "key3": "status1", + } + + actualData := utils.FilterByStatus(data, status) + + assert.Equal(t, expectedData, actualData) + }) + + t.Run("Test FilterByStatus with empty data", func(t *testing.T) { + data := map[string]string{} + status := "status1" + + expectedData := map[string]string{} + + actualData := utils.FilterByStatus(data, status) + + assert.Equal(t, expectedData, actualData) + }) +} + +func Test_BeautifyMD(t *testing.T) { + t.Run("Test BeautifyMD under normal conditions", func(t *testing.T) { + data := []byte("# i am a test") + r, err := glamour.NewTermRenderer( + glamour.WithAutoStyle(), + glamour.WithWordWrap(120), + glamour.WithAutoStyle(), + ) + require.NoError(t, err) + + expected, err := r.Render(string(data)) + require.NoError(t, err) + + actual, err := utils.BeautifyMD(data) + + require.NoError(t, err) + assert.Equal(t, expected, actual) + }) + + t.Run("Test BeautifyMD with empty data", func(t *testing.T) { + data := []byte("") + r, err := glamour.NewTermRenderer( + glamour.WithAutoStyle(), + glamour.WithWordWrap(120), + glamour.WithAutoStyle(), + ) + require.NoError(t, err) + + expected, err := r.Render(string(data)) + require.NoError(t, err) + + actual, err := utils.BeautifyMD(data) + + require.NoError(t, err) + assert.Equal(t, expected, actual) + }) +} + +func Test_ReadREADME(t *testing.T) { + t.Run("Test ReadREADME under normal conditions", func(t *testing.T) { + expected := []byte{} + + t.Cleanup(func() { + err := db.DeleteDb(dbname) + require.NoError(t, err) + _ = os.RemoveAll(projectPath) + }) + + err := os.Mkdir(projectPath, 0755) + require.NoError(t, err) + f, err := os.Create(projectPath + "/README.md") + require.NoError(t, err) + f.Close() + + err = db.WriteToDB(dbname, map[string]string{projectName: projectPath}, pkg.ProjectPaths) + require.NoError(t, err) + + actual, err := utils.ReadREADME(dbname, projectName) + + assert.Equal(t, expected, actual) + require.NoError(t, err) + }) + t.Run("Test ReadREADME with alias", func(t *testing.T) { + alias := "project_alias" + expected := []byte{} + + t.Cleanup(func() { + err := db.DeleteDb(dbname) + require.NoError(t, err) + _ = os.RemoveAll(projectPath) + }) + + err := os.Mkdir(projectPath, 0755) + require.NoError(t, err) + f, err := os.Create(projectPath + "/README.md") + require.NoError(t, err) + f.Close() + + err = db.WriteToDB(dbname, map[string]string{projectName: alias}, pkg.ProjectAliasBucket) + require.NoError(t, err) + err = db.WriteToDB(dbname, map[string]string{projectName: projectPath, alias: projectPath}, pkg.ProjectPaths) + require.NoError(t, err) + + actual, err := utils.ReadREADME(dbname, projectName) + + assert.Equal(t, expected, actual) + require.NoError(t, err) + }) + + t.Run("Test ReadREADME with empty project name", func(t *testing.T) { + projectName := "" + expected := []byte(nil) + + actual, err := utils.ReadREADME(dbname, projectName) + + assert.Equal(t, expected, actual) + require.ErrorIs(t, err, utils.ErrGetProject) + }) + + t.Run("Test ReadREADME with invalid README file name", func(t *testing.T) { + expected := []byte(nil) + + t.Cleanup(func() { + err := db.DeleteDb(dbname) + require.NoError(t, err) + _ = os.RemoveAll(projectPath) + }) + + err := os.Mkdir(projectPath, 0755) + require.NoError(t, err) + f, err := os.Create(projectPath + "/README.txt") + require.NoError(t, err) + f.Close() + + err = db.WriteToDB(dbname, map[string]string{projectName: projectPath}, pkg.ProjectPaths) + require.NoError(t, err) + + actual, err := utils.ReadREADME(dbname, projectName) + + assert.Equal(t, expected, actual) + require.ErrorIs(t, err, utils.ErrReadREADME) + }) +} + +func Test_GetLastModifiedTime(t *testing.T) { + t.Run("Test GetLastModifiedTime under normal conditions: case Today modification", func(t *testing.T) { + t.Cleanup(func() { + err := db.DeleteDb(dbname) + require.NoError(t, err) + _ = os.RemoveAll(projectPath) + }) + + err := os.Mkdir(projectPath, 0755) + require.NoError(t, err) + f, err := os.Create(projectPath + "/README.md") + require.NoError(t, err) + f.Close() + + err = db.WriteToDB(dbname, map[string]string{projectName: projectPath}, pkg.ProjectPaths) + require.NoError(t, err) + + actual := utils.GetLastModifiedTime(dbname, projectName) + + assert.NotEqual(t, expectedMsg, actual) + assert.Contains(t, actual, "Today") + assert.NotEmpty(t, actual) + }) + + t.Run("Test GetLastModifiedTime under normal conditions: case Yesterday modification", func(t *testing.T) { + t.Cleanup(func() { + err := db.DeleteDb(dbname) + require.NoError(t, err) + _ = os.RemoveAll(projectPath) + }) + + err := os.Mkdir(projectPath, 0755) + require.NoError(t, err) + f, err := os.Create(projectPath + "/README.md") + require.NoError(t, err) + err = os.Chtimes(projectPath+"/README.md", time.Now().AddDate(0, 0, -1), time.Now().AddDate(0, 0, -1)) + require.NoError(t, err) + f.Close() + + err = db.WriteToDB(dbname, map[string]string{projectName: projectPath}, pkg.ProjectPaths) + require.NoError(t, err) + + actual := utils.GetLastModifiedTime(dbname, projectName) + + assert.NotEqual(t, expectedMsg, actual) + assert.Contains(t, actual, "Yesterday") + assert.NotEmpty(t, actual) + }) + t.Run("Test GetLastModifiedTime under normal conditions: case old modification", func(t *testing.T) { + t.Cleanup(func() { + err := db.DeleteDb(dbname) + require.NoError(t, err) + _ = os.RemoveAll(projectPath) + }) + + err := os.Mkdir(projectPath, 0755) + require.NoError(t, err) + f, err := os.Create(projectPath + "/README.md") + require.NoError(t, err) + err = os.Chtimes(projectPath+"/README.md", time.Now().AddDate(0, 0, -5), time.Now().AddDate(0, 0, -5)) + require.NoError(t, err) + f.Close() + + err = db.WriteToDB(dbname, map[string]string{projectName: projectPath}, pkg.ProjectPaths) + require.NoError(t, err) + + actual := utils.GetLastModifiedTime(dbname, projectName) + + assert.NotEqual(t, expectedMsg, actual) + assert.NotEmpty(t, actual) + }) + t.Run("Test GetLastModifiedTime with invalid project", func(t *testing.T) { + projectPath := "./invalid_project" + + actual := utils.GetLastModifiedTime(dbname, projectPath) + + assert.Equal(t, expectedMsg, actual) + }) +}