cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm leaflet readability golang
at main 204 lines 5.2 kB view raw
1package ui 2 3import ( 4 "bytes" 5 "context" 6 "fmt" 7 "strings" 8 "testing" 9 10 "github.com/stormlightlabs/noteleaf/internal/repo" 11) 12 13// mockProjectRepository implements ProjectRepository for testing 14type mockProjectRepository struct { 15 projects []repo.ProjectSummary 16 err error 17} 18 19func (m *mockProjectRepository) GetProjects(ctx context.Context) ([]repo.ProjectSummary, error) { 20 if m.err != nil { 21 return nil, m.err 22 } 23 return m.projects, nil 24} 25 26func TestProjectAdapter(t *testing.T) { 27 t.Run("ProjectSummaryRecord", func(t *testing.T) { 28 summary := repo.ProjectSummary{ 29 Name: "Test Project", 30 TaskCount: 5, 31 } 32 record := &ProjectSummaryRecord{summary: summary} 33 34 t.Run("GetField", func(t *testing.T) { 35 tests := []struct { 36 field string 37 expected any 38 name string 39 }{ 40 {"name", "Test Project", "should return project name"}, 41 {"task_count", 5, "should return task count"}, 42 {"unknown", "", "should return empty string for unknown field"}, 43 } 44 45 for _, tt := range tests { 46 t.Run(tt.name, func(t *testing.T) { 47 result := record.GetField(tt.field) 48 if result != tt.expected { 49 t.Errorf("GetField(%q) = %v, want %v", tt.field, result, tt.expected) 50 } 51 }) 52 } 53 }) 54 55 t.Run("ModelInterface", func(t *testing.T) { 56 if record.GetID() != 5 { 57 t.Errorf("GetID() = %d, want 5", record.GetID()) 58 } 59 60 if record.GetTableName() != "projects" { 61 t.Errorf("GetTableName() = %q, want 'projects'", record.GetTableName()) 62 } 63 64 Expect.AssertZeroTime(t, record.GetCreatedAt, "GetCreatedAt") 65 Expect.AssertZeroTime(t, record.GetUpdatedAt, "GetUpdatedAt") 66 }) 67 }) 68 69 t.Run("ProjectDataSource", func(t *testing.T) { 70 t.Run("Load", func(t *testing.T) { 71 projects := []repo.ProjectSummary{ 72 {Name: "Project 1", TaskCount: 5}, 73 {Name: "Project 2", TaskCount: 3}, 74 } 75 repo := &mockProjectRepository{projects: projects} 76 source := &ProjectDataSource{repo: repo} 77 78 records, err := source.Load(context.Background(), DataOptions{}) 79 if err != nil { 80 t.Fatalf("Load() failed: %v", err) 81 } 82 83 if len(records) != 2 { 84 t.Errorf("Load() returned %d records, want 2", len(records)) 85 } 86 87 if records[0].GetField("name") != "Project 1" { 88 t.Errorf("First record name = %v, want 'Project 1'", records[0].GetField("name")) 89 } 90 91 if records[0].GetField("task_count") != 5 { 92 t.Errorf("First record task_count = %v, want 5", records[0].GetField("task_count")) 93 } 94 }) 95 96 t.Run("Load_Error", func(t *testing.T) { 97 testErr := fmt.Errorf("test error") 98 repo := &mockProjectRepository{err: testErr} 99 source := &ProjectDataSource{repo: repo} 100 101 _, err := source.Load(context.Background(), DataOptions{}) 102 if err != testErr { 103 t.Errorf("Load() error = %v, want %v", err, testErr) 104 } 105 }) 106 107 t.Run("Count", func(t *testing.T) { 108 projects := []repo.ProjectSummary{ 109 {Name: "Project 1", TaskCount: 5}, 110 {Name: "Project 2", TaskCount: 3}, 111 {Name: "Project 3", TaskCount: 1}, 112 } 113 repo := &mockProjectRepository{projects: projects} 114 source := &ProjectDataSource{repo: repo} 115 116 count, err := source.Count(context.Background(), DataOptions{}) 117 if err != nil { 118 t.Fatalf("Count() failed: %v", err) 119 } 120 121 if count != 3 { 122 t.Errorf("Count() = %d, want 3", count) 123 } 124 }) 125 126 t.Run("Count_Error", func(t *testing.T) { 127 testErr := fmt.Errorf("test error") 128 repo := &mockProjectRepository{err: testErr} 129 source := &ProjectDataSource{repo: repo} 130 131 _, err := source.Count(context.Background(), DataOptions{}) 132 if err != testErr { 133 t.Errorf("Count() error = %v, want %v", err, testErr) 134 } 135 }) 136 }) 137 138 t.Run("NewProjectDataTable", func(t *testing.T) { 139 repo := &mockProjectRepository{ 140 projects: []repo.ProjectSummary{ 141 {Name: "Test Project", TaskCount: 2}, 142 }, 143 } 144 145 opts := DataTableOptions{ 146 Output: &bytes.Buffer{}, 147 Input: strings.NewReader("q\n"), 148 Static: true, 149 } 150 151 table := NewProjectDataTable(repo, opts) 152 if table == nil { 153 t.Fatal("NewProjectDataTable() returned nil") 154 } 155 156 emptyOpts := DataTableOptions{ 157 Output: &bytes.Buffer{}, 158 Input: strings.NewReader("q\n"), 159 Static: true, 160 } 161 162 table2 := NewProjectDataTable(repo, emptyOpts) 163 if table2 == nil { 164 t.Fatal("NewProjectDataTable() with empty opts returned nil") 165 } 166 167 err := table.Browse(context.Background()) 168 if err != nil { 169 t.Errorf("Browse() failed: %v", err) 170 } 171 }) 172 173 t.Run("NewProjectListFromTable", func(t *testing.T) { 174 repo := &mockProjectRepository{ 175 projects: []repo.ProjectSummary{ 176 {Name: "Test Project", TaskCount: 1}, 177 }, 178 } 179 180 output := &bytes.Buffer{} 181 input := strings.NewReader("q\n") 182 183 table := NewProjectListFromTable(repo, output, input, true) 184 if table == nil { 185 t.Fatal("NewProjectListFromTable() returned nil") 186 } 187 188 err := table.Browse(context.Background()) 189 if err != nil { 190 t.Errorf("Browse() failed: %v", err) 191 } 192 193 outputStr := output.String() 194 if !strings.Contains(outputStr, "Projects") { 195 t.Error("Output should contain 'Projects' title") 196 } 197 if !strings.Contains(outputStr, "Test Project") { 198 t.Error("Output should contain project name") 199 } 200 if !strings.Contains(outputStr, "1 task") { 201 t.Error("Output should contain formatted task count") 202 } 203 }) 204}