@@ -2,6 +2,7 @@ package config
22
33import (
44 "encoding/json"
5+ "fmt"
56 "os"
67 "path/filepath"
78 "runtime"
@@ -16,6 +17,9 @@ func TestDefaultValues(t *testing.T) {
1617 t .Parallel ()
1718 cfg := Default ()
1819
20+ if cfg .ConfigVersion != currentConfigVersion {
21+ t .Errorf ("ConfigVersion = %d, want %d" , cfg .ConfigVersion , currentConfigVersion )
22+ }
1923 if cfg .DefaultShell != "" {
2024 t .Errorf ("DefaultShell = %q, want empty" , cfg .DefaultShell )
2125 }
@@ -77,6 +81,7 @@ func TestConfigJSONRoundTrip(t *testing.T) {
7781 DefaultTerminal : "alacritty" ,
7882 DefaultTimeRange : "7d" ,
7983 DefaultSort : "created" ,
84+ DefaultSortOrder : "asc" ,
8085 DefaultPivot : "repo" ,
8186 ShowPreview : false ,
8287 MaxSessions : 50 ,
@@ -112,6 +117,9 @@ func TestConfigJSONRoundTrip(t *testing.T) {
112117 if restored .DefaultSort != original .DefaultSort {
113118 t .Errorf ("DefaultSort = %q, want %q" , restored .DefaultSort , original .DefaultSort )
114119 }
120+ if restored .DefaultSortOrder != original .DefaultSortOrder {
121+ t .Errorf ("DefaultSortOrder = %q, want %q" , restored .DefaultSortOrder , original .DefaultSortOrder )
122+ }
115123 if restored .DefaultPivot != original .DefaultPivot {
116124 t .Errorf ("DefaultPivot = %q, want %q" , restored .DefaultPivot , original .DefaultPivot )
117125 }
@@ -1171,3 +1179,164 @@ func TestPreviewPositionConstants(t *testing.T) {
11711179 t .Errorf ("PreviewPositionTop = %q, want 'top'" , PreviewPositionTop )
11721180 }
11731181}
1182+
1183+ func TestEffectiveSortOrder_DefaultsToDesc (t * testing.T ) {
1184+ t .Parallel ()
1185+ cfg := Default ()
1186+ if got := cfg .EffectiveSortOrder (); got != SortOrderDesc {
1187+ t .Errorf ("EffectiveSortOrder() = %q, want %q" , got , SortOrderDesc )
1188+ }
1189+ }
1190+
1191+ func TestEffectiveSortOrder_RespectsAsc (t * testing.T ) {
1192+ t .Parallel ()
1193+ cfg := Default ()
1194+ cfg .DefaultSortOrder = SortOrderAsc
1195+ if got := cfg .EffectiveSortOrder (); got != SortOrderAsc {
1196+ t .Errorf ("EffectiveSortOrder() = %q, want %q" , got , SortOrderAsc )
1197+ }
1198+ }
1199+
1200+ func TestEffectiveSortOrder_InvalidFallsBackToDesc (t * testing.T ) {
1201+ t .Parallel ()
1202+ cfg := Default ()
1203+ cfg .DefaultSortOrder = "invalid"
1204+ if got := cfg .EffectiveSortOrder (); got != SortOrderDesc {
1205+ t .Errorf ("EffectiveSortOrder() = %q, want %q" , got , SortOrderDesc )
1206+ }
1207+ }
1208+
1209+ func TestDefaultSortOrderOmittedFromJSON (t * testing.T ) {
1210+ t .Parallel ()
1211+ cfg := Default ()
1212+ data , err := json .Marshal (cfg )
1213+ if err != nil {
1214+ t .Fatalf ("Marshal: %v" , err )
1215+ }
1216+ var raw map [string ]any
1217+ if err := json .Unmarshal (data , & raw ); err != nil {
1218+ t .Fatalf ("Unmarshal: %v" , err )
1219+ }
1220+ if _ , ok := raw ["default_sort_order" ]; ok {
1221+ t .Error ("default_sort_order should be omitted from JSON when empty" )
1222+ }
1223+ }
1224+
1225+ func TestDefaultSortOrderPreservedOnLoad (t * testing.T ) {
1226+ t .Parallel ()
1227+ jsonData := `{"default_sort_order": "asc"}`
1228+ cfg := Default ()
1229+ if err := json .Unmarshal ([]byte (jsonData ), cfg ); err != nil {
1230+ t .Fatalf ("Unmarshal: %v" , err )
1231+ }
1232+ if cfg .DefaultSortOrder != SortOrderAsc {
1233+ t .Errorf ("DefaultSortOrder = %q, want %q" , cfg .DefaultSortOrder , SortOrderAsc )
1234+ }
1235+ }
1236+
1237+ // ---------------------------------------------------------------------------
1238+ // Config version and migration tests
1239+ // ---------------------------------------------------------------------------
1240+
1241+ func TestDefaultConfigVersion (t * testing.T ) {
1242+ t .Parallel ()
1243+ cfg := Default ()
1244+ if cfg .ConfigVersion != currentConfigVersion {
1245+ t .Errorf ("ConfigVersion = %d, want %d" , cfg .ConfigVersion , currentConfigVersion )
1246+ }
1247+ }
1248+
1249+ func TestMigrate_V0LaunchInPlaceToLaunchMode (t * testing.T ) {
1250+ t .Parallel ()
1251+ cfg := & Config {
1252+ ConfigVersion : 0 ,
1253+ LaunchInPlace : true ,
1254+ }
1255+ migrate (cfg )
1256+ if cfg .LaunchMode != LaunchModeInPlace {
1257+ t .Errorf ("LaunchMode = %q, want %q" , cfg .LaunchMode , LaunchModeInPlace )
1258+ }
1259+ }
1260+
1261+ func TestMigrate_V0LaunchInPlaceSkippedWhenLaunchModeSet (t * testing.T ) {
1262+ t .Parallel ()
1263+ cfg := & Config {
1264+ ConfigVersion : 0 ,
1265+ LaunchInPlace : true ,
1266+ LaunchMode : LaunchModeTab ,
1267+ }
1268+ migrate (cfg )
1269+ if cfg .LaunchMode != LaunchModeTab {
1270+ t .Errorf ("LaunchMode = %q, want %q (should not be overwritten)" , cfg .LaunchMode , LaunchModeTab )
1271+ }
1272+ }
1273+
1274+ func TestLoad_MigratesAndPersistsVersion (t * testing.T ) {
1275+ dir := withTempConfigDir (t )
1276+
1277+ // Write a v0 config (no config_version field).
1278+ path := filepath .Join (dir , "dispatch" , configFileName )
1279+ if err := os .MkdirAll (filepath .Dir (path ), 0o755 ); err != nil {
1280+ t .Fatalf ("MkdirAll: %v" , err )
1281+ }
1282+ content := `{"launchInPlace": true, "default_shell": "fish"}`
1283+ if err := os .WriteFile (path , []byte (content ), 0o644 ); err != nil {
1284+ t .Fatalf ("WriteFile: %v" , err )
1285+ }
1286+
1287+ cfg , err := Load ()
1288+ if err != nil {
1289+ t .Fatalf ("Load: %v" , err )
1290+ }
1291+
1292+ // Migration should have run.
1293+ if cfg .LaunchMode != LaunchModeInPlace {
1294+ t .Errorf ("LaunchMode = %q, want %q after migration" , cfg .LaunchMode , LaunchModeInPlace )
1295+ }
1296+ // User settings should be preserved.
1297+ if cfg .DefaultShell != "fish" {
1298+ t .Errorf ("DefaultShell = %q, want 'fish' (should survive migration)" , cfg .DefaultShell )
1299+ }
1300+ // Version should be updated.
1301+ if cfg .ConfigVersion != currentConfigVersion {
1302+ t .Errorf ("ConfigVersion = %d, want %d" , cfg .ConfigVersion , currentConfigVersion )
1303+ }
1304+
1305+ // File should have been re-written with the new version.
1306+ reloaded , err := os .ReadFile (path )
1307+ if err != nil {
1308+ t .Fatalf ("ReadFile after migration: %v" , err )
1309+ }
1310+ var raw map [string ]any
1311+ if err := json .Unmarshal (reloaded , & raw ); err != nil {
1312+ t .Fatalf ("Unmarshal re-written config: %v" , err )
1313+ }
1314+ if v , ok := raw ["config_version" ]; ! ok || int (v .(float64 )) != currentConfigVersion {
1315+ t .Errorf ("Persisted config_version = %v, want %d" , v , currentConfigVersion )
1316+ }
1317+ }
1318+
1319+ func TestLoad_SkipsMigrationWhenCurrent (t * testing.T ) {
1320+ dir := withTempConfigDir (t )
1321+
1322+ // Write a current-version config.
1323+ path := filepath .Join (dir , "dispatch" , configFileName )
1324+ if err := os .MkdirAll (filepath .Dir (path ), 0o755 ); err != nil {
1325+ t .Fatalf ("MkdirAll: %v" , err )
1326+ }
1327+ content := fmt .Sprintf (`{"config_version": %d, "default_shell": "zsh"}` , currentConfigVersion )
1328+ if err := os .WriteFile (path , []byte (content ), 0o644 ); err != nil {
1329+ t .Fatalf ("WriteFile: %v" , err )
1330+ }
1331+
1332+ cfg , err := Load ()
1333+ if err != nil {
1334+ t .Fatalf ("Load: %v" , err )
1335+ }
1336+ if cfg .DefaultShell != "zsh" {
1337+ t .Errorf ("DefaultShell = %q, want 'zsh'" , cfg .DefaultShell )
1338+ }
1339+ if cfg .ConfigVersion != currentConfigVersion {
1340+ t .Errorf ("ConfigVersion = %d, want %d" , cfg .ConfigVersion , currentConfigVersion )
1341+ }
1342+ }
0 commit comments