|
1 | 1 | package backup |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "context" |
4 | 5 | "regexp" |
5 | 6 | "testing" |
6 | 7 | "time" |
@@ -126,3 +127,97 @@ func TestFindPreviousWatchBackup(t *testing.T) { |
126 | 127 | assert.Equal(t, "", prevName) |
127 | 128 | assert.Equal(t, "", prevType) |
128 | 129 | } |
| 130 | + |
| 131 | +func TestWatchScheduleTemplateRE(t *testing.T) { |
| 132 | + ctx := context.Background() |
| 133 | + // {type} and {time:layout} placeholders shall turn into \S+ |
| 134 | + assert.Equal(t, `x-\S+-\S+`, watchScheduleTemplatePrepareRE.ReplaceAllString("x-{type}-{time:20060102150405}", `\S+`)) |
| 135 | + |
| 136 | + // template without `{` shall skip ApplyMacros ClickHouse query and compile as anchored regexp |
| 137 | + b := NewBackuper(config.DefaultConfig()) |
| 138 | + templateRE, err := b.watchScheduleTemplateRE(ctx, "daily-static-name") |
| 139 | + require.NoError(t, err) |
| 140 | + assert.True(t, templateRE.MatchString("daily-static-name")) |
| 141 | + assert.False(t, templateRE.MatchString("prefix-daily-static-name-suffix")) |
| 142 | + |
| 143 | + // invalid regexp in template shall trigger compile error |
| 144 | + _, err = b.watchScheduleTemplateRE(ctx, "bad[template") |
| 145 | + require.Error(t, err) |
| 146 | + assert.Contains(t, err.Error(), "can't compile regexp") |
| 147 | +} |
| 148 | + |
| 149 | +func newTestScheduleBackuper(schedules config.WatchSchedules) *Backuper { |
| 150 | + cfg := config.DefaultConfig() |
| 151 | + // no `{` in template, so ApplyMacros shall not query ClickHouse |
| 152 | + cfg.General.WatchBackupNameTemplate = "static-name" |
| 153 | + cfg.General.WatchSchedules = schedules |
| 154 | + return NewBackuper(cfg) |
| 155 | +} |
| 156 | + |
| 157 | +func TestBuildWatchScheduleStates(t *testing.T) { |
| 158 | + ctx := context.Background() |
| 159 | + |
| 160 | + // success, continue chain from the last matching non-broken remote backup |
| 161 | + b := newTestScheduleBackuper(config.WatchSchedules{{Name: "daily", Full: "0 0 * * *", Increment: "*/15 * * * *"}}) |
| 162 | + remoteBackups := []storage.Backup{ |
| 163 | + {BackupMetadata: metadata.BackupMetadata{BackupName: "daily-static-name"}}, |
| 164 | + } |
| 165 | + states, err := b.buildWatchScheduleStates(ctx, remoteBackups) |
| 166 | + require.NoError(t, err) |
| 167 | + require.Len(t, states, 1) |
| 168 | + assert.Equal(t, "daily-static-name", states[0].template) |
| 169 | + assert.Equal(t, "daily-static-name", states[0].prevBackupName) |
| 170 | + assert.Equal(t, "full", states[0].prevBackupType) |
| 171 | + assert.NotNil(t, states[0].incrementCron) |
| 172 | + |
| 173 | + // invalid `full` cron expression |
| 174 | + b = newTestScheduleBackuper(config.WatchSchedules{{Name: "daily", Full: "garbage"}}) |
| 175 | + _, err = b.buildWatchScheduleStates(ctx, nil) |
| 176 | + require.Error(t, err) |
| 177 | + assert.Contains(t, err.Error(), "invalid `full` cron expression") |
| 178 | + |
| 179 | + // invalid `increment` cron expression |
| 180 | + b = newTestScheduleBackuper(config.WatchSchedules{{Name: "daily", Full: "0 0 * * *", Increment: "garbage"}}) |
| 181 | + _, err = b.buildWatchScheduleStates(ctx, nil) |
| 182 | + require.Error(t, err) |
| 183 | + assert.Contains(t, err.Error(), "invalid `increment` cron expression") |
| 184 | + |
| 185 | + // `full` fires more often than `increment`, warning branch shall not fail state build |
| 186 | + b = newTestScheduleBackuper(config.WatchSchedules{{Name: "daily", Full: "*/15 * * * *", Increment: "0 * * * *"}}) |
| 187 | + states, err = b.buildWatchScheduleStates(ctx, nil) |
| 188 | + require.NoError(t, err) |
| 189 | + require.Len(t, states, 1) |
| 190 | + |
| 191 | + // invalid regexp in watch_backup_name_template propagates from watchScheduleTemplateRE |
| 192 | + b = newTestScheduleBackuper(config.WatchSchedules{{Name: "daily", Full: "0 0 * * *"}}) |
| 193 | + b.cfg.General.WatchBackupNameTemplate = "bad[" |
| 194 | + _, err = b.buildWatchScheduleStates(ctx, nil) |
| 195 | + require.Error(t, err) |
| 196 | + assert.Contains(t, err.Error(), "can't compile regexp") |
| 197 | +} |
| 198 | + |
| 199 | +// newTestUnreachableClickHouseBackuper - port 1 is closed, ch.Connect inside GetRemoteBackups shall fail fast |
| 200 | +func newTestUnreachableClickHouseBackuper() *Backuper { |
| 201 | + cfg := config.DefaultConfig() |
| 202 | + cfg.ClickHouse.Host = "127.0.0.1" |
| 203 | + cfg.ClickHouse.Port = 1 |
| 204 | + cfg.General.WatchSchedules = config.WatchSchedules{{Name: "daily", Full: "0 0 * * *"}} |
| 205 | + b := NewBackuper(cfg) |
| 206 | + // Connect retries forever by design (issue #857), fail on the first Ping error instead of hanging the test |
| 207 | + b.ch.BreakConnectOnError = true |
| 208 | + return b |
| 209 | +} |
| 210 | + |
| 211 | +func TestNewWatchScheduleStatesRemoteError(t *testing.T) { |
| 212 | + b := newTestUnreachableClickHouseBackuper() |
| 213 | + _, err := b.newWatchScheduleStates(context.Background()) |
| 214 | + require.Error(t, err) |
| 215 | + assert.Contains(t, err.Error(), "newWatchScheduleStates GetRemoteBackups") |
| 216 | +} |
| 217 | + |
| 218 | +func TestWatchWithSchedulesStatesError(t *testing.T) { |
| 219 | + b := newTestUnreachableClickHouseBackuper() |
| 220 | + err := b.watchWithSchedules(context.Background(), "", "", "", nil, "", nil, nil, false, false, false, false, false, false, "test", 0, nil, nil) |
| 221 | + require.Error(t, err) |
| 222 | + assert.Contains(t, err.Error(), "watchWithSchedules newWatchScheduleStates") |
| 223 | +} |
0 commit comments