refactor(preview): replace status magic strings with PreviewStatus typed enum
- commit
- bffa8f635b36de29d17dec8d63eb0f4dad91462c
- author
- brice · · 2026-03-18 21:25 UTC
- committer
- brice · · 2026-03-18 19:52 UTC
- parent
- 1aaeb90
- signature
- unsigned
5 files · +30 -21
M internal/adapters/in/cli/preview.go +3 -3
| @@ -319,7 +319,7 @@ func waitForPreview(ctx context.Context, out io.Writer, name, ttl string, noData | |||
| 319 | 319 | continue // not found yet, keep polling | |
| 320 | 320 | } | |
| 321 | 321 | switch p.Status { | |
| 322 | - | case "running": | |
| 322 | + | case domain.PreviewStatusRunning: | |
| 323 | 323 | scheme := "http" | |
| 324 | 324 | if p.HTTPS { | |
| 325 | 325 | scheme = "https" | |
| @@ -329,10 +329,10 @@ func waitForPreview(ctx context.Context, out io.Writer, name, ttl string, noData | |||
| 329 | 329 | return err | |
| 330 | 330 | } | |
| 331 | 331 | return cliWriteLine(out, cliRenderMeta("URL:", previewURL)) | |
| 332 | - | case "failed": | |
| 332 | + | case domain.PreviewStatusFailed: | |
| 333 | 333 | return fmt.Errorf("preview deployment failed") | |
| 334 | 334 | } | |
| 335 | - | // status is "deploying", keep polling | |
| 335 | + | // status is PreviewStatusDeploying, keep polling | |
| 336 | 336 | } | |
| 337 | 337 | } | |
| 338 | 338 | } | |
| @@ -319,7 +319,7 @@ func waitForPreview(ctx context.Context, out io.Writer, name, ttl string, noData | |||||
| 319 | continue // not found yet, keep polling | 319 | continue // not found yet, keep polling | ||
| 320 | } | 320 | } | ||
| 321 | switch p.Status { | 321 | switch p.Status { | ||
| 322 | - | case "running": | 322 | + | case domain.PreviewStatusRunning: |
| 323 | scheme := "http" | 323 | scheme := "http" | ||
| 324 | if p.HTTPS { | 324 | if p.HTTPS { | ||
| 325 | scheme = "https" | 325 | scheme = "https" | ||
| @@ -329,10 +329,10 @@ func waitForPreview(ctx context.Context, out io.Writer, name, ttl string, noData | |||||
| 329 | return err | 329 | return err | ||
| 330 | } | 330 | } | ||
| 331 | return cliWriteLine(out, cliRenderMeta("URL:", previewURL)) | 331 | return cliWriteLine(out, cliRenderMeta("URL:", previewURL)) | ||
| 332 | - | case "failed": | 332 | + | case domain.PreviewStatusFailed: |
| 333 | return fmt.Errorf("preview deployment failed") | 333 | return fmt.Errorf("preview deployment failed") | ||
| 334 | } | 334 | } | ||
| 335 | - | // status is "deploying", keep polling | 335 | + | // status is PreviewStatusDeploying, keep polling |
| 336 | } | 336 | } | ||
| 337 | } | 337 | } | ||
| 338 | } | 338 | } | ||
M internal/domain/preview.go +19 -10
| @@ -2,18 +2,27 @@ package domain | |||
| 2 | 2 | ||
| 3 | 3 | import "time" | |
| 4 | 4 | ||
| 5 | + | // PreviewStatus represents the lifecycle state of a preview environment. | |
| 6 | + | type PreviewStatus string | |
| 7 | + | ||
| 8 | + | const ( | |
| 9 | + | PreviewStatusDeploying PreviewStatus = "deploying" | |
| 10 | + | PreviewStatusRunning PreviewStatus = "running" | |
| 11 | + | PreviewStatusFailed PreviewStatus = "failed" | |
| 12 | + | ) | |
| 13 | + | ||
| 5 | 14 | // PreviewRoute represents an ephemeral preview environment. | |
| 6 | 15 | type PreviewRoute struct { | |
| 7 | - | Domain string `json:"domain"` | |
| 8 | - | Image string `json:"image"` | |
| 9 | - | BaseRoute string `json:"base_route"` | |
| 10 | - | Name string `json:"name"` | |
| 11 | - | CreatedAt time.Time `json:"created_at"` | |
| 12 | - | ExpiresAt time.Time `json:"expires_at"` | |
| 13 | - | HTTPS bool `json:"https"` | |
| 14 | - | Status string `json:"status"` // "deploying", "running", "failed" | |
| 15 | - | Volumes []string `json:"volumes"` | |
| 16 | - | Containers []string `json:"containers"` | |
| 16 | + | Domain string `json:"domain"` | |
| 17 | + | Image string `json:"image"` | |
| 18 | + | BaseRoute string `json:"base_route"` | |
| 19 | + | Name string `json:"name"` | |
| 20 | + | CreatedAt time.Time `json:"created_at"` | |
| 21 | + | ExpiresAt time.Time `json:"expires_at"` | |
| 22 | + | HTTPS bool `json:"https"` | |
| 23 | + | Status PreviewStatus `json:"status"` | |
| 24 | + | Volumes []string `json:"volumes"` | |
| 25 | + | Containers []string `json:"containers"` | |
| 17 | 26 | } | |
| 18 | 27 | ||
| 19 | 28 | // IsExpired returns true if the preview has exceeded its TTL. | |
| @@ -2,18 +2,27 @@ package domain | |||||
| 2 | 2 | ||||
| 3 | import "time" | 3 | import "time" | ||
| 4 | 4 | ||||
| 5 | + | // PreviewStatus represents the lifecycle state of a preview environment. | |||
| 6 | + | type PreviewStatus string | |||
| 7 | + | ||||
| 8 | + | const ( | |||
| 9 | + | PreviewStatusDeploying PreviewStatus = "deploying" | |||
| 10 | + | PreviewStatusRunning PreviewStatus = "running" | |||
| 11 | + | PreviewStatusFailed PreviewStatus = "failed" | |||
| 12 | + | ) | |||
| 13 | + | ||||
| 5 | // PreviewRoute represents an ephemeral preview environment. | 14 | // PreviewRoute represents an ephemeral preview environment. | ||
| 6 | type PreviewRoute struct { | 15 | type PreviewRoute struct { | ||
| 7 | - | Domain string `json:"domain"` | 16 | + | Domain string `json:"domain"` |
| 8 | - | Image string `json:"image"` | 17 | + | Image string `json:"image"` |
| 9 | - | BaseRoute string `json:"base_route"` | 18 | + | BaseRoute string `json:"base_route"` |
| 10 | - | Name string `json:"name"` | 19 | + | Name string `json:"name"` |
| 11 | - | CreatedAt time.Time `json:"created_at"` | 20 | + | CreatedAt time.Time `json:"created_at"` |
| 12 | - | ExpiresAt time.Time `json:"expires_at"` | 21 | + | ExpiresAt time.Time `json:"expires_at"` |
| 13 | - | HTTPS bool `json:"https"` | 22 | + | HTTPS bool `json:"https"` |
| 14 | - | Status string `json:"status"` // "deploying", "running", "failed" | 23 | + | Status PreviewStatus `json:"status"` |
| 15 | - | Volumes []string `json:"volumes"` | 24 | + | Volumes []string `json:"volumes"` |
| 16 | - | Containers []string `json:"containers"` | 25 | + | Containers []string `json:"containers"` |
| 17 | } | 26 | } | ||
| 18 | 27 | ||||
| 19 | // IsExpired returns true if the preview has exceeded its TTL. | 28 | // IsExpired returns true if the preview has exceeded its TTL. | ||
M internal/usecase/auto/preview/integration_test.go +1 -1
| @@ -176,6 +176,6 @@ func TestPreviewLifecycle_WithDeploy(t *testing.T) { | |||
| 176 | 176 | all, err := svc.List(ctx) | |
| 177 | 177 | require.NoError(t, err) | |
| 178 | 178 | require.Len(t, all, 1) | |
| 179 | - | assert.Equal(t, "running", all[0].Status) | |
| 179 | + | assert.Equal(t, domain.PreviewStatusRunning, all[0].Status) | |
| 180 | 180 | assert.Equal(t, []string{"gordon-myapp--test-feat.example.com"}, all[0].Containers) | |
| 181 | 181 | } | |
| @@ -176,6 +176,6 @@ func TestPreviewLifecycle_WithDeploy(t *testing.T) { | |||||
| 176 | all, err := svc.List(ctx) | 176 | all, err := svc.List(ctx) | ||
| 177 | require.NoError(t, err) | 177 | require.NoError(t, err) | ||
| 178 | require.Len(t, all, 1) | 178 | require.Len(t, all, 1) | ||
| 179 | - | assert.Equal(t, "running", all[0].Status) | 179 | + | assert.Equal(t, domain.PreviewStatusRunning, all[0].Status) |
| 180 | assert.Equal(t, []string{"gordon-myapp--test-feat.example.com"}, all[0].Containers) | 180 | assert.Equal(t, []string{"gordon-myapp--test-feat.example.com"}, all[0].Containers) | ||
| 181 | } | 181 | } | ||
M internal/usecase/auto/preview/service.go +4 -4
| @@ -338,7 +338,7 @@ func (s *Service) CreatePreview(ctx context.Context, req CreatePreviewRequest) e | |||
| 338 | 338 | CreatedAt: now, | |
| 339 | 339 | ExpiresAt: now.Add(req.PreviewConfig.TTL), | |
| 340 | 340 | HTTPS: req.HTTPS, | |
| 341 | - | Status: "deploying", | |
| 341 | + | Status: domain.PreviewStatusDeploying, | |
| 342 | 342 | } | |
| 343 | 343 | ||
| 344 | 344 | if err := s.Add(ctx, preview); err != nil { | |
| @@ -361,7 +361,7 @@ func (s *Service) CreatePreview(ctx context.Context, req CreatePreviewRequest) e | |||
| 361 | 361 | Image: imageRef, | |
| 362 | 362 | HTTPS: req.HTTPS, | |
| 363 | 363 | }); err != nil { | |
| 364 | - | preview.Status = "failed" | |
| 364 | + | preview.Status = domain.PreviewStatusFailed | |
| 365 | 365 | _ = s.Update(ctx, preview) | |
| 366 | 366 | return fmt.Errorf("register preview route %q: %w", req.Domain, err) | |
| 367 | 367 | } | |
| @@ -376,14 +376,14 @@ func (s *Service) CreatePreview(ctx context.Context, req CreatePreviewRequest) e | |||
| 376 | 376 | HTTPS: req.HTTPS, | |
| 377 | 377 | }) | |
| 378 | 378 | if err != nil { | |
| 379 | - | preview.Status = "failed" | |
| 379 | + | preview.Status = domain.PreviewStatusFailed | |
| 380 | 380 | _ = s.Update(ctx, preview) | |
| 381 | 381 | return fmt.Errorf("deploy preview %q: %w", req.Name, err) | |
| 382 | 382 | } | |
| 383 | 383 | preview.Containers = []string{container.Name} | |
| 384 | 384 | } | |
| 385 | 385 | ||
| 386 | - | preview.Status = "running" | |
| 386 | + | preview.Status = domain.PreviewStatusRunning | |
| 387 | 387 | if err := s.Update(ctx, preview); err != nil { | |
| 388 | 388 | return fmt.Errorf("finalize preview %q: %w", req.Name, err) | |
| 389 | 389 | } | |
| @@ -338,7 +338,7 @@ func (s *Service) CreatePreview(ctx context.Context, req CreatePreviewRequest) e | |||||
| 338 | CreatedAt: now, | 338 | CreatedAt: now, | ||
| 339 | ExpiresAt: now.Add(req.PreviewConfig.TTL), | 339 | ExpiresAt: now.Add(req.PreviewConfig.TTL), | ||
| 340 | HTTPS: req.HTTPS, | 340 | HTTPS: req.HTTPS, | ||
| 341 | - | Status: "deploying", | 341 | + | Status: domain.PreviewStatusDeploying, |
| 342 | } | 342 | } | ||
| 343 | 343 | ||||
| 344 | if err := s.Add(ctx, preview); err != nil { | 344 | if err := s.Add(ctx, preview); err != nil { | ||
| @@ -361,7 +361,7 @@ func (s *Service) CreatePreview(ctx context.Context, req CreatePreviewRequest) e | |||||
| 361 | Image: imageRef, | 361 | Image: imageRef, | ||
| 362 | HTTPS: req.HTTPS, | 362 | HTTPS: req.HTTPS, | ||
| 363 | }); err != nil { | 363 | }); err != nil { | ||
| 364 | - | preview.Status = "failed" | 364 | + | preview.Status = domain.PreviewStatusFailed |
| 365 | _ = s.Update(ctx, preview) | 365 | _ = s.Update(ctx, preview) | ||
| 366 | return fmt.Errorf("register preview route %q: %w", req.Domain, err) | 366 | return fmt.Errorf("register preview route %q: %w", req.Domain, err) | ||
| 367 | } | 367 | } | ||
| @@ -376,14 +376,14 @@ func (s *Service) CreatePreview(ctx context.Context, req CreatePreviewRequest) e | |||||
| 376 | HTTPS: req.HTTPS, | 376 | HTTPS: req.HTTPS, | ||
| 377 | }) | 377 | }) | ||
| 378 | if err != nil { | 378 | if err != nil { | ||
| 379 | - | preview.Status = "failed" | 379 | + | preview.Status = domain.PreviewStatusFailed |
| 380 | _ = s.Update(ctx, preview) | 380 | _ = s.Update(ctx, preview) | ||
| 381 | return fmt.Errorf("deploy preview %q: %w", req.Name, err) | 381 | return fmt.Errorf("deploy preview %q: %w", req.Name, err) | ||
| 382 | } | 382 | } | ||
| 383 | preview.Containers = []string{container.Name} | 383 | preview.Containers = []string{container.Name} | ||
| 384 | } | 384 | } | ||
| 385 | 385 | ||||
| 386 | - | preview.Status = "running" | 386 | + | preview.Status = domain.PreviewStatusRunning |
| 387 | if err := s.Update(ctx, preview); err != nil { | 387 | if err := s.Update(ctx, preview); err != nil { | ||
| 388 | return fmt.Errorf("finalize preview %q: %w", req.Name, err) | 388 | return fmt.Errorf("finalize preview %q: %w", req.Name, err) | ||
| 389 | } | 389 | } | ||
M internal/usecase/auto/preview/service_test.go +3 -3
| @@ -111,7 +111,7 @@ func TestPreviewService_GetExpired(t *testing.T) { | |||
| 111 | 111 | func TestPreviewService_Update(t *testing.T) { | |
| 112 | 112 | store := &fakeStore{ | |
| 113 | 113 | previews: []domain.PreviewRoute{ | |
| 114 | - | {Name: "feat", Domain: "myapp--feat.example.com", Status: "deploying"}, | |
| 114 | + | {Name: "feat", Domain: "myapp--feat.example.com", Status: domain.PreviewStatusDeploying}, | |
| 115 | 115 | }, | |
| 116 | 116 | } | |
| 117 | 117 | svc := NewService(store, 48*time.Hour) | |
| @@ -120,7 +120,7 @@ func TestPreviewService_Update(t *testing.T) { | |||
| 120 | 120 | updated := domain.PreviewRoute{ | |
| 121 | 121 | Name: "feat", | |
| 122 | 122 | Domain: "myapp--feat.example.com", | |
| 123 | - | Status: "running", | |
| 123 | + | Status: domain.PreviewStatusRunning, | |
| 124 | 124 | Containers: []string{"gordon-myapp--feat.example.com"}, | |
| 125 | 125 | } | |
| 126 | 126 | err := svc.Update(t.Context(), updated) | |
| @@ -128,7 +128,7 @@ func TestPreviewService_Update(t *testing.T) { | |||
| 128 | 128 | ||
| 129 | 129 | p, err := svc.Get(t.Context(), "feat") | |
| 130 | 130 | require.NoError(t, err) | |
| 131 | - | assert.Equal(t, "running", p.Status) | |
| 131 | + | assert.Equal(t, domain.PreviewStatusRunning, p.Status) | |
| 132 | 132 | assert.Equal(t, []string{"gordon-myapp--feat.example.com"}, p.Containers) | |
| 133 | 133 | } | |
| 134 | 134 | ||
| @@ -111,7 +111,7 @@ func TestPreviewService_GetExpired(t *testing.T) { | |||||
| 111 | func TestPreviewService_Update(t *testing.T) { | 111 | func TestPreviewService_Update(t *testing.T) { | ||
| 112 | store := &fakeStore{ | 112 | store := &fakeStore{ | ||
| 113 | previews: []domain.PreviewRoute{ | 113 | previews: []domain.PreviewRoute{ | ||
| 114 | - | {Name: "feat", Domain: "myapp--feat.example.com", Status: "deploying"}, | 114 | + | {Name: "feat", Domain: "myapp--feat.example.com", Status: domain.PreviewStatusDeploying}, |
| 115 | }, | 115 | }, | ||
| 116 | } | 116 | } | ||
| 117 | svc := NewService(store, 48*time.Hour) | 117 | svc := NewService(store, 48*time.Hour) | ||
| @@ -120,7 +120,7 @@ func TestPreviewService_Update(t *testing.T) { | |||||
| 120 | updated := domain.PreviewRoute{ | 120 | updated := domain.PreviewRoute{ | ||
| 121 | Name: "feat", | 121 | Name: "feat", | ||
| 122 | Domain: "myapp--feat.example.com", | 122 | Domain: "myapp--feat.example.com", | ||
| 123 | - | Status: "running", | 123 | + | Status: domain.PreviewStatusRunning, |
| 124 | Containers: []string{"gordon-myapp--feat.example.com"}, | 124 | Containers: []string{"gordon-myapp--feat.example.com"}, | ||
| 125 | } | 125 | } | ||
| 126 | err := svc.Update(t.Context(), updated) | 126 | err := svc.Update(t.Context(), updated) | ||
| @@ -128,7 +128,7 @@ func TestPreviewService_Update(t *testing.T) { | |||||
| 128 | 128 | ||||
| 129 | p, err := svc.Get(t.Context(), "feat") | 129 | p, err := svc.Get(t.Context(), "feat") | ||
| 130 | require.NoError(t, err) | 130 | require.NoError(t, err) | ||
| 131 | - | assert.Equal(t, "running", p.Status) | 131 | + | assert.Equal(t, domain.PreviewStatusRunning, p.Status) |
| 132 | assert.Equal(t, []string{"gordon-myapp--feat.example.com"}, p.Containers) | 132 | assert.Equal(t, []string{"gordon-myapp--feat.example.com"}, p.Containers) | ||
| 133 | } | 133 | } | ||
| 134 | 134 | ||||