|
6 | 6 | package base |
7 | 7 |
|
8 | 8 | import ( |
| 9 | + "errors" |
9 | 10 | "os" |
10 | 11 | "strings" |
| 12 | + "sync" |
11 | 13 | "testing" |
12 | 14 | "time" |
13 | 15 |
|
@@ -86,38 +88,69 @@ func TestGetTriggerNames(t *testing.T) { |
86 | 88 | } |
87 | 89 |
|
88 | 90 | func TestValidateGhostTriggerLengthBelowMaxLength(t *testing.T) { |
| 91 | + // Tests simulate the real call pattern: GetGhostTriggerName first, then validate the result. |
89 | 92 | { |
| 93 | + // Short trigger name with suffix appended: well under 64 chars |
90 | 94 | context := NewMigrationContext() |
91 | 95 | context.TriggerSuffix = "_gho" |
92 | | - require.True(t, context.ValidateGhostTriggerLengthBelowMaxLength("my_trigger")) |
| 96 | + ghostName := context.GetGhostTriggerName("my_trigger") // "my_trigger_gho" = 14 chars |
| 97 | + require.True(t, context.ValidateGhostTriggerLengthBelowMaxLength(ghostName)) |
93 | 98 | } |
94 | 99 | { |
| 100 | + // 64-char original + "_ghost" suffix = 70 chars → exceeds limit |
95 | 101 | context := NewMigrationContext() |
96 | 102 | context.TriggerSuffix = "_ghost" |
97 | | - require.False(t, context.ValidateGhostTriggerLengthBelowMaxLength(strings.Repeat("my_trigger_ghost", 4))) // 64 characters + "_ghost" |
| 103 | + ghostName := context.GetGhostTriggerName(strings.Repeat("my_trigger_ghost", 4)) // 64 + 6 = 70 |
| 104 | + require.False(t, context.ValidateGhostTriggerLengthBelowMaxLength(ghostName)) |
98 | 105 | } |
99 | 106 | { |
| 107 | + // 48-char original + "_ghost" suffix = 54 chars → valid |
100 | 108 | context := NewMigrationContext() |
101 | 109 | context.TriggerSuffix = "_ghost" |
102 | | - require.True(t, context.ValidateGhostTriggerLengthBelowMaxLength(strings.Repeat("my_trigger_ghost", 3))) // 48 characters + "_ghost" |
| 110 | + ghostName := context.GetGhostTriggerName(strings.Repeat("my_trigger_ghost", 3)) // 48 + 6 = 54 |
| 111 | + require.True(t, context.ValidateGhostTriggerLengthBelowMaxLength(ghostName)) |
103 | 112 | } |
104 | 113 | { |
| 114 | + // RemoveTriggerSuffix: 64-char name ending in "_ghost" → suffix removed → 58 chars → valid |
105 | 115 | context := NewMigrationContext() |
106 | 116 | context.TriggerSuffix = "_ghost" |
107 | 117 | context.RemoveTriggerSuffix = true |
108 | | - require.True(t, context.ValidateGhostTriggerLengthBelowMaxLength(strings.Repeat("my_trigger_ghost", 4))) // 64 characters + "_ghost" removed |
| 118 | + ghostName := context.GetGhostTriggerName(strings.Repeat("my_trigger_ghost", 4)) // suffix removed → 58 |
| 119 | + require.True(t, context.ValidateGhostTriggerLengthBelowMaxLength(ghostName)) |
109 | 120 | } |
110 | 121 | { |
| 122 | + // RemoveTriggerSuffix: name doesn't end in suffix → suffix appended → 65 + 6 = 71 chars → exceeds |
111 | 123 | context := NewMigrationContext() |
112 | 124 | context.TriggerSuffix = "_ghost" |
113 | 125 | context.RemoveTriggerSuffix = true |
114 | | - require.False(t, context.ValidateGhostTriggerLengthBelowMaxLength(strings.Repeat("my_trigger_ghost", 4)+"X")) // 65 characters + "_ghost" not removed |
| 126 | + ghostName := context.GetGhostTriggerName(strings.Repeat("my_trigger_ghost", 4) + "X") // no match, appended → 71 |
| 127 | + require.False(t, context.ValidateGhostTriggerLengthBelowMaxLength(ghostName)) |
115 | 128 | } |
116 | 129 | { |
| 130 | + // RemoveTriggerSuffix: 70-char name ending in "_ghost" → suffix removed → 64 chars → exactly at limit → valid |
117 | 131 | context := NewMigrationContext() |
118 | 132 | context.TriggerSuffix = "_ghost" |
119 | 133 | context.RemoveTriggerSuffix = true |
120 | | - require.True(t, context.ValidateGhostTriggerLengthBelowMaxLength(strings.Repeat("my_trigger_ghost", 4)+"_ghost")) // 70 characters + last "_ghost" removed |
| 134 | + ghostName := context.GetGhostTriggerName(strings.Repeat("my_trigger_ghost", 4) + "_ghost") // suffix removed → 64 |
| 135 | + require.True(t, context.ValidateGhostTriggerLengthBelowMaxLength(ghostName)) |
| 136 | + } |
| 137 | + { |
| 138 | + // Edge case: exactly 64 chars after transformation → valid (boundary test) |
| 139 | + context := NewMigrationContext() |
| 140 | + context.TriggerSuffix = "_ght" |
| 141 | + originalName := strings.Repeat("x", 60) // 60 chars |
| 142 | + ghostName := context.GetGhostTriggerName(originalName) // 60 + 4 = 64 |
| 143 | + require.Equal(t, 64, len(ghostName)) |
| 144 | + require.True(t, context.ValidateGhostTriggerLengthBelowMaxLength(ghostName)) |
| 145 | + } |
| 146 | + { |
| 147 | + // Edge case: 65 chars after transformation → exceeds (boundary test) |
| 148 | + context := NewMigrationContext() |
| 149 | + context.TriggerSuffix = "_ght" |
| 150 | + originalName := strings.Repeat("x", 61) // 61 chars |
| 151 | + ghostName := context.GetGhostTriggerName(originalName) // 61 + 4 = 65 |
| 152 | + require.Equal(t, 65, len(ghostName)) |
| 153 | + require.False(t, context.ValidateGhostTriggerLengthBelowMaxLength(ghostName)) |
121 | 154 | } |
122 | 155 | } |
123 | 156 |
|
@@ -182,3 +215,58 @@ func TestReadConfigFile(t *testing.T) { |
182 | 215 | } |
183 | 216 | } |
184 | 217 | } |
| 218 | + |
| 219 | +func TestSetAbortError_StoresFirstError(t *testing.T) { |
| 220 | + ctx := NewMigrationContext() |
| 221 | + |
| 222 | + err1 := errors.New("first error") |
| 223 | + err2 := errors.New("second error") |
| 224 | + |
| 225 | + ctx.SetAbortError(err1) |
| 226 | + ctx.SetAbortError(err2) |
| 227 | + |
| 228 | + got := ctx.GetAbortError() |
| 229 | + if got != err1 { //nolint:errorlint // Testing pointer equality for sentinel error |
| 230 | + t.Errorf("Expected first error %v, got %v", err1, got) |
| 231 | + } |
| 232 | +} |
| 233 | + |
| 234 | +func TestSetAbortError_ThreadSafe(t *testing.T) { |
| 235 | + ctx := NewMigrationContext() |
| 236 | + |
| 237 | + var wg sync.WaitGroup |
| 238 | + errs := []error{ |
| 239 | + errors.New("error 1"), |
| 240 | + errors.New("error 2"), |
| 241 | + errors.New("error 3"), |
| 242 | + } |
| 243 | + |
| 244 | + // Launch 3 goroutines trying to set error concurrently |
| 245 | + for _, err := range errs { |
| 246 | + wg.Add(1) |
| 247 | + go func(e error) { |
| 248 | + defer wg.Done() |
| 249 | + ctx.SetAbortError(e) |
| 250 | + }(err) |
| 251 | + } |
| 252 | + |
| 253 | + wg.Wait() |
| 254 | + |
| 255 | + // Should store exactly one of the errors |
| 256 | + got := ctx.GetAbortError() |
| 257 | + if got == nil { |
| 258 | + t.Fatal("Expected error to be stored, got nil") |
| 259 | + } |
| 260 | + |
| 261 | + // Verify it's one of the errors we sent |
| 262 | + found := false |
| 263 | + for _, err := range errs { |
| 264 | + if got == err { //nolint:errorlint // Testing pointer equality for sentinel error |
| 265 | + found = true |
| 266 | + break |
| 267 | + } |
| 268 | + } |
| 269 | + if !found { |
| 270 | + t.Errorf("Stored error %v not in list of sent errors", got) |
| 271 | + } |
| 272 | +} |
0 commit comments