default_update_test.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package e2e
  2. import (
  3. "context"
  4. "encoding/json"
  5. "os"
  6. "path/filepath"
  7. "syscall"
  8. "testing"
  9. "github.com/cline/cli/pkg/common"
  10. )
  11. // 2. Multi-instance start: default_instance remains the first started.
  12. func TestMultiInstanceDefaultUnchanged(t *testing.T) {
  13. _ = setTempClineDir(t)
  14. ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
  15. defer cancel()
  16. // Start first instance and wait healthy
  17. _ = mustRunCLI(ctx, t, "instance", "new")
  18. out1 := listInstancesJSON(ctx, t)
  19. if len(out1.CoreInstances) != 1 {
  20. t.Fatalf("expected 1 instance, got %d", len(out1.CoreInstances))
  21. }
  22. firstAddr := out1.CoreInstances[0].Address
  23. waitForAddressHealthy(t, firstAddr, defaultTimeout)
  24. // Start second instance
  25. _ = mustRunCLI(ctx, t, "instance", "new")
  26. out2 := listInstancesJSON(ctx, t)
  27. if len(out2.CoreInstances) < 2 {
  28. t.Fatalf("expected at least 2 instances, got %d", len(out2.CoreInstances))
  29. }
  30. // Default should remain the first started address
  31. if out2.DefaultInstance != firstAddr {
  32. t.Fatalf("default changed; expected %s, got %s", firstAddr, out2.DefaultInstance)
  33. }
  34. }
  35. // 6. Default.json update after removal of current default
  36. func TestDefaultJsonUpdateAfterRemoval(t *testing.T) {
  37. _ = setTempClineDir(t)
  38. ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
  39. defer cancel()
  40. // Start two instances
  41. _ = mustRunCLI(ctx, t, "instance", "new")
  42. _ = mustRunCLI(ctx, t, "instance", "new")
  43. out := listInstancesJSON(ctx, t)
  44. if len(out.CoreInstances) < 2 {
  45. t.Fatalf("expected at least 2 instances, got %d", len(out.CoreInstances))
  46. }
  47. // Choose second as new default
  48. target := out.CoreInstances[1]
  49. waitForAddressHealthy(t, target.Address, defaultTimeout)
  50. // Set as default
  51. _ = mustRunCLI(ctx, t, "instance", "use", target.Address)
  52. // Verify default switched
  53. out = listInstancesJSON(ctx, t)
  54. if out.DefaultInstance != target.Address {
  55. t.Fatalf("default_instance not updated to %s (got %s)", target.Address, out.DefaultInstance)
  56. }
  57. // Kill the default instance using runtime PID discovery
  58. corePID := getCorePID(t, target.Address)
  59. if corePID <= 0 {
  60. t.Fatalf("could not find PID for core process at %s", target.Address)
  61. }
  62. t.Logf("Killing cline-core process PID %d for instance %s", corePID, target.Address)
  63. if err := syscall.Kill(corePID, syscall.SIGKILL); err != nil {
  64. t.Fatalf("kill pid %d: %v", corePID, err)
  65. }
  66. // Wait for removal
  67. waitForAddressRemoved(t, target.Address, longTimeout)
  68. // Clean up dangling host process (SIGKILL leaves these behind by design)
  69. t.Logf("Cleaning up dangling host process on port %d", target.HostPort())
  70. findAndKillHostProcess(t, target.HostPort())
  71. // Ensure default_instance updated to another available instance (or removed if none remain)
  72. out = listInstancesJSON(ctx, t)
  73. // If there are instances left, default_instance must be one of them
  74. if len(out.CoreInstances) > 0 {
  75. found := false
  76. for _, it := range out.CoreInstances {
  77. if out.DefaultInstance == it.Address {
  78. found = true
  79. break
  80. }
  81. }
  82. if !found {
  83. t.Fatalf("default_instance %s not set to an existing instance after removal", out.DefaultInstance)
  84. }
  85. } else {
  86. // No instances remain; cli-default-instance.json should be removed
  87. clineDir := getClineDir(t)
  88. defPath := filepath.Join(clineDir, common.SETTINGS_SUBFOLDER, "settings", "cli-default-instance.json")
  89. if _, err := os.Stat(defPath); err == nil {
  90. t.Fatalf("expected cli-default-instance.json removed when no instances remain")
  91. }
  92. }
  93. // Also verify cli-default-instance.json on disk reflects the in-memory default (if any)
  94. clineDir := getClineDir(t)
  95. defPath := filepath.Join(clineDir, common.SETTINGS_SUBFOLDER, "settings", "cli-default-instance.json")
  96. if len(out.CoreInstances) > 0 {
  97. raw, err := os.ReadFile(defPath)
  98. if err != nil {
  99. t.Fatalf("read cli-default-instance.json: %v", err)
  100. }
  101. var tmp struct {
  102. DefaultInstance string `json:"default_instance"`
  103. }
  104. if err := json.Unmarshal(raw, &tmp); err != nil {
  105. t.Fatalf("unmarshal cli-default-instance.json: %v", err)
  106. }
  107. if tmp.DefaultInstance != out.DefaultInstance {
  108. t.Fatalf("cli-default-instance.json mismatch: file=%s list=%s", tmp.DefaultInstance, out.DefaultInstance)
  109. }
  110. }
  111. }
  112. // 11. SQLite database missing (edge): list succeeds and returns empty set
  113. func TestRegistryDirMissingEdge(t *testing.T) {
  114. clineDir := setTempClineDir(t)
  115. // Remove the settings directory entirely (which contains locks.db)
  116. settingsDir := filepath.Join(clineDir, common.SETTINGS_SUBFOLDER)
  117. if err := os.RemoveAll(settingsDir); err != nil {
  118. t.Fatalf("RemoveAll(%s): %v", common.SETTINGS_SUBFOLDER, err)
  119. }
  120. // Listing should succeed and return empty results
  121. ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
  122. defer cancel()
  123. out := listInstancesJSON(ctx, t)
  124. if len(out.CoreInstances) != 0 {
  125. t.Fatalf("expected 0 instances after removing %s dir, got %d", common.SETTINGS_SUBFOLDER, len(out.CoreInstances))
  126. }
  127. // Ensure cli-default-instance.json not present
  128. defPath := filepath.Join(clineDir, common.SETTINGS_SUBFOLDER, "settings", "cli-default-instance.json")
  129. if _, err := os.Stat(defPath); err == nil {
  130. t.Fatalf("expected no cli-default-instance.json after removing %s dir", common.SETTINGS_SUBFOLDER)
  131. }
  132. }