cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm leaflet readability golang
29
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 310 lines 8.1 kB view raw
1package store 2 3import ( 4 "database/sql" 5 "fmt" 6 "os" 7 "path/filepath" 8 "runtime" 9 "strings" 10 "testing" 11 12 "github.com/stormlightlabs/noteleaf/internal/shared" 13) 14 15func withTempDirs(t *testing.T) string { 16 t.Helper() 17 tempDir, cleanup := shared.CreateTempDir("noteleaf-db-test-*", t) 18 t.Cleanup(func() { cleanup() }) 19 20 origConfig, origData := GetConfigDir, GetDataDir 21 GetConfigDir = func() (string, error) { return tempDir, nil } 22 GetDataDir = func() (string, error) { return tempDir, nil } 23 t.Cleanup(func() { 24 GetConfigDir, GetDataDir = origConfig, origData 25 }) 26 27 return tempDir 28} 29 30func TestNewDatabase(t *testing.T) { 31 tempDir := withTempDirs(t) 32 33 t.Run("creates database file", func(t *testing.T) { 34 db, err := NewDatabase() 35 if err != nil { 36 t.Fatalf("NewDatabase failed: %v", err) 37 } 38 defer db.Close() 39 40 dbPath := filepath.Join(tempDir, "noteleaf.db") 41 if _, err := os.Stat(dbPath); os.IsNotExist(err) { 42 t.Errorf("expected database file at %s", dbPath) 43 } 44 }) 45 46 t.Run("foreign keys enabled", func(t *testing.T) { 47 db, _ := NewDatabase() 48 defer db.Close() 49 50 var fk int 51 if err := db.QueryRow("PRAGMA foreign_keys").Scan(&fk); err != nil { 52 t.Fatalf("query failed: %v", err) 53 } 54 if fk != 1 { 55 t.Errorf("expected foreign_keys=1, got %d", fk) 56 } 57 }) 58 59 t.Run("WAL enabled", func(t *testing.T) { 60 db, _ := NewDatabase() 61 defer db.Close() 62 63 var mode string 64 if err := db.QueryRow("PRAGMA journal_mode").Scan(&mode); err != nil { 65 t.Fatalf("query failed: %v", err) 66 } 67 if strings.ToLower(mode) != "wal" { 68 t.Errorf("expected wal, got %s", mode) 69 } 70 }) 71} 72 73func TestNewDatabase_ErrorPaths(t *testing.T) { 74 t.Run("sql.Open fails", func(t *testing.T) { 75 orig := sqlOpen 76 sqlOpen = func(driver, dsn string) (*sql.DB, error) { 77 return nil, fmt.Errorf("boom") 78 } 79 t.Cleanup(func() { sqlOpen = orig }) 80 81 _, err := NewDatabase() 82 if err == nil || !strings.Contains(err.Error(), "failed to open database") { 83 t.Errorf("expected open error, got %v", err) 84 } 85 }) 86 87 t.Run("foreign_keys pragma fails", func(t *testing.T) { 88 orig := pragmaExec 89 pragmaExec = func(db *sql.DB, stmt string) (sql.Result, error) { 90 if strings.Contains(stmt, "foreign_keys") { 91 return nil, fmt.Errorf("fk fail") 92 } 93 return orig(db, stmt) 94 } 95 t.Cleanup(func() { pragmaExec = orig }) 96 97 _, err := NewDatabase() 98 if err == nil || !strings.Contains(err.Error(), "failed to enable foreign keys") { 99 t.Errorf("expected foreign key error, got %v", err) 100 } 101 }) 102 103 t.Run("WAL pragma fails", func(t *testing.T) { 104 orig := pragmaExec 105 pragmaExec = func(db *sql.DB, stmt string) (sql.Result, error) { 106 if strings.Contains(stmt, "journal_mode") { 107 return nil, fmt.Errorf("wal fail") 108 } 109 return orig(db, stmt) 110 } 111 t.Cleanup(func() { pragmaExec = orig }) 112 113 _, err := NewDatabase() 114 if err == nil || !strings.Contains(err.Error(), "failed to enable WAL mode") { 115 t.Errorf("expected WAL error, got %v", err) 116 } 117 }) 118 119 t.Run("migration runner fails", func(t *testing.T) { 120 orig := createMigrationRunner 121 createMigrationRunner = func(db *sql.DB, fs FileSystem) *MigrationRunner { 122 return &MigrationRunner{runFn: func() error { return fmt.Errorf("migration fail") }} 123 } 124 t.Cleanup(func() { createMigrationRunner = orig }) 125 126 _, err := NewDatabase() 127 if err == nil || !strings.Contains(err.Error(), "failed to run migrations") { 128 t.Errorf("expected migration error, got %v", err) 129 } 130 }) 131 132 t.Run("permission denied on config dir", func(t *testing.T) { 133 if runtime.GOOS == "windows" { 134 t.Skip("not reliable on Windows") 135 } 136 dir := withTempDirs(t) 137 os.Chmod(dir, 0555) // make read-only 138 defer os.Chmod(dir, 0755) 139 140 GetConfigDir = func() (string, error) { return dir, nil } 141 142 _, err := NewDatabase() 143 if err == nil { 144 t.Error("expected mkdir fail due to permission denied") 145 } 146 }) 147} 148 149func TestGetConfigDir_AllBranches(t *testing.T) { 150 tmp := t.TempDir() 151 152 t.Run("windows success", func(t *testing.T) { 153 origGOOS := getRuntime 154 getRuntime = func() string { return "windows" } 155 defer func() { getRuntime = origGOOS }() 156 157 os.Setenv("APPDATA", tmp) 158 defer os.Unsetenv("APPDATA") 159 160 dir, err := GetConfigDir() 161 if err != nil { 162 t.Fatalf("unexpected error: %v", err) 163 } 164 expected := filepath.Join(tmp, "noteleaf") 165 if dir != expected { 166 t.Errorf("expected %s, got %s", expected, dir) 167 } 168 }) 169 170 t.Run("windows missing APPDATA", func(t *testing.T) { 171 origGOOS := getRuntime 172 getRuntime = func() string { return "windows" } 173 defer func() { getRuntime = origGOOS }() 174 175 os.Unsetenv("APPDATA") 176 177 _, err := GetConfigDir() 178 if err == nil || !strings.Contains(err.Error(), "APPDATA") { 179 t.Errorf("expected APPDATA error, got %v", err) 180 } 181 }) 182 183 t.Run("darwin success", func(t *testing.T) { 184 origGOOS, origHome := getRuntime, getHomeDir 185 getRuntime = func() string { return "darwin" } 186 getHomeDir = func() (string, error) { return tmp, nil } 187 defer func() { 188 getRuntime = origGOOS 189 getHomeDir = origHome 190 }() 191 192 dir, err := GetConfigDir() 193 if err != nil { 194 t.Fatalf("unexpected error: %v", err) 195 } 196 expected := filepath.Join(tmp, "Library", "Application Support", "noteleaf") 197 if dir != expected { 198 t.Errorf("expected %s, got %s", expected, dir) 199 } 200 }) 201 202 t.Run("linux default with XDG_CONFIG_HOME unset", func(t *testing.T) { 203 origGOOS, origHome := getRuntime, getHomeDir 204 getRuntime = func() string { return "linux" } 205 getHomeDir = func() (string, error) { return tmp, nil } 206 defer func() { 207 getRuntime = origGOOS 208 getHomeDir = origHome 209 }() 210 211 os.Unsetenv("XDG_CONFIG_HOME") 212 213 dir, err := GetConfigDir() 214 if err != nil { 215 t.Fatalf("unexpected error: %v", err) 216 } 217 expected := filepath.Join(tmp, ".config", "noteleaf") 218 if dir != expected { 219 t.Errorf("expected %s, got %s", expected, dir) 220 } 221 }) 222} 223 224func TestGetDataDir_AllBranches(t *testing.T) { 225 tmp := t.TempDir() 226 227 t.Run("NOTELEAF_DATA_DIR overrides", func(t *testing.T) { 228 os.Setenv("NOTELEAF_DATA_DIR", tmp) 229 defer os.Unsetenv("NOTELEAF_DATA_DIR") 230 231 dir, err := GetDataDir() 232 if err != nil { 233 t.Fatalf("unexpected error: %v", err) 234 } 235 if dir != tmp { 236 t.Errorf("expected %s, got %s", tmp, dir) 237 } 238 }) 239 240 t.Run("windows success", func(t *testing.T) { 241 origGOOS := getRuntime 242 getRuntime = func() string { return "windows" } 243 defer func() { getRuntime = origGOOS }() 244 245 os.Setenv("LOCALAPPDATA", tmp) 246 defer os.Unsetenv("LOCALAPPDATA") 247 248 dir, err := GetDataDir() 249 if err != nil { 250 t.Fatalf("unexpected error: %v", err) 251 } 252 expected := filepath.Join(tmp, "noteleaf") 253 if dir != expected { 254 t.Errorf("expected %s, got %s", expected, dir) 255 } 256 }) 257 258 t.Run("windows missing LOCALAPPDATA", func(t *testing.T) { 259 origGOOS := getRuntime 260 getRuntime = func() string { return "windows" } 261 defer func() { getRuntime = origGOOS }() 262 263 os.Unsetenv("LOCALAPPDATA") 264 265 _, err := GetDataDir() 266 if err == nil || !strings.Contains(err.Error(), "LOCALAPPDATA") { 267 t.Errorf("expected LOCALAPPDATA error, got %v", err) 268 } 269 }) 270 271 t.Run("darwin success", func(t *testing.T) { 272 origGOOS, origHome := getRuntime, getHomeDir 273 getRuntime = func() string { return "darwin" } 274 getHomeDir = func() (string, error) { return tmp, nil } 275 defer func() { 276 getRuntime = origGOOS 277 getHomeDir = origHome 278 }() 279 280 dir, err := GetDataDir() 281 if err != nil { 282 t.Fatalf("unexpected error: %v", err) 283 } 284 expected := filepath.Join(tmp, "Library", "Application Support", "noteleaf") 285 if dir != expected { 286 t.Errorf("expected %s, got %s", expected, dir) 287 } 288 }) 289 290 t.Run("linux default with XDG_DATA_HOME unset", func(t *testing.T) { 291 origGOOS, origHome := getRuntime, getHomeDir 292 getRuntime = func() string { return "linux" } 293 getHomeDir = func() (string, error) { return tmp, nil } 294 defer func() { 295 getRuntime = origGOOS 296 getHomeDir = origHome 297 }() 298 299 os.Unsetenv("XDG_DATA_HOME") 300 301 dir, err := GetDataDir() 302 if err != nil { 303 t.Fatalf("unexpected error: %v", err) 304 } 305 expected := filepath.Join(tmp, ".local", "share", "noteleaf") 306 if dir != expected { 307 t.Errorf("expected %s, got %s", expected, dir) 308 } 309 }) 310}