fix(admin): handle tags not-found and extend restart tests
- commit
- af868088d4ec08ba2467f43ffdab5abcf88164d4
- author
- brice · · 2026-02-02 19:11 UTC
- parent
- 1f87088
- signature
- unsigned
5 files · +159 -1
M internal/adapters/in/http/admin/handler.go +5 -0
| @@ -15,6 +15,7 @@ import ( | |||
| 15 | 15 | "github.com/bnema/gordon/internal/boundaries/in" | |
| 16 | 16 | "github.com/bnema/gordon/internal/boundaries/out" | |
| 17 | 17 | "github.com/bnema/gordon/internal/domain" | |
| 18 | + | "github.com/bnema/gordon/internal/usecase/registry" | |
| 18 | 19 | "github.com/bnema/gordon/pkg/validation" | |
| 19 | 20 | ) | |
| 20 | 21 | ||
| @@ -837,6 +838,10 @@ func (h *Handler) handleTags(w http.ResponseWriter, r *http.Request, path string | |||
| 837 | 838 | ||
| 838 | 839 | tags, err := h.registrySvc.ListTags(ctx, repository) | |
| 839 | 840 | if err != nil { | |
| 841 | + | if errors.Is(err, registry.ErrRepositoryNotFound) { | |
| 842 | + | h.sendError(w, http.StatusNotFound, "repository not found") | |
| 843 | + | return | |
| 844 | + | } | |
| 840 | 845 | h.sendError(w, http.StatusInternalServerError, "failed to list tags") | |
| 841 | 846 | return | |
| 842 | 847 | } | |
| @@ -15,6 +15,7 @@ import ( | |||||
| 15 | "github.com/bnema/gordon/internal/boundaries/in" | 15 | "github.com/bnema/gordon/internal/boundaries/in" | ||
| 16 | "github.com/bnema/gordon/internal/boundaries/out" | 16 | "github.com/bnema/gordon/internal/boundaries/out" | ||
| 17 | "github.com/bnema/gordon/internal/domain" | 17 | "github.com/bnema/gordon/internal/domain" | ||
| 18 | + | "github.com/bnema/gordon/internal/usecase/registry" | |||
| 18 | "github.com/bnema/gordon/pkg/validation" | 19 | "github.com/bnema/gordon/pkg/validation" | ||
| 19 | ) | 20 | ) | ||
| 20 | 21 | ||||
| @@ -837,6 +838,10 @@ func (h *Handler) handleTags(w http.ResponseWriter, r *http.Request, path string | |||||
| 837 | 838 | ||||
| 838 | tags, err := h.registrySvc.ListTags(ctx, repository) | 839 | tags, err := h.registrySvc.ListTags(ctx, repository) | ||
| 839 | if err != nil { | 840 | if err != nil { | ||
| 841 | + | if errors.Is(err, registry.ErrRepositoryNotFound) { | |||
| 842 | + | h.sendError(w, http.StatusNotFound, "repository not found") | |||
| 843 | + | return | |||
| 844 | + | } | |||
| 840 | h.sendError(w, http.StatusInternalServerError, "failed to list tags") | 845 | h.sendError(w, http.StatusInternalServerError, "failed to list tags") | ||
| 841 | return | 846 | return | ||
| 842 | } | 847 | } | ||
M internal/adapters/in/http/admin/handler_test.go +95 -0
| @@ -915,6 +915,53 @@ func TestHandler_Restart_GenericError(t *testing.T) { | |||
| 915 | 915 | assert.NotContains(t, rec.Body.String(), "secret details") | |
| 916 | 916 | } | |
| 917 | 917 | ||
| 918 | + | func TestHandler_Restart_Success(t *testing.T) { | |
| 919 | + | configSvc := inmocks.NewMockConfigService(t) | |
| 920 | + | authSvc := inmocks.NewMockAuthService(t) | |
| 921 | + | containerSvc := inmocks.NewMockContainerService(t) | |
| 922 | + | secretSvc := inmocks.NewMockSecretService(t) | |
| 923 | + | ||
| 924 | + | handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger()) | |
| 925 | + | ||
| 926 | + | tests := []struct { | |
| 927 | + | name string | |
| 928 | + | path string | |
| 929 | + | withAttachments bool | |
| 930 | + | }{ | |
| 931 | + | { | |
| 932 | + | name: "without attachments", | |
| 933 | + | path: "/admin/restart/test.example.com", | |
| 934 | + | withAttachments: false, | |
| 935 | + | }, | |
| 936 | + | { | |
| 937 | + | name: "with attachments", | |
| 938 | + | path: "/admin/restart/test.example.com?attachments=true", | |
| 939 | + | withAttachments: true, | |
| 940 | + | }, | |
| 941 | + | } | |
| 942 | + | ||
| 943 | + | for _, tt := range tests { | |
| 944 | + | t.Run(tt.name, func(t *testing.T) { | |
| 945 | + | containerSvc.EXPECT().Restart(mock.Anything, "test.example.com", tt.withAttachments).Return(nil) | |
| 946 | + | ||
| 947 | + | req := httptest.NewRequest("POST", tt.path, nil) | |
| 948 | + | req = req.WithContext(ctxWithScopes("admin:config:write")) | |
| 949 | + | rec := httptest.NewRecorder() | |
| 950 | + | ||
| 951 | + | handler.ServeHTTP(rec, req) | |
| 952 | + | ||
| 953 | + | assert.Equal(t, http.StatusOK, rec.Code) | |
| 954 | + | var response struct { | |
| 955 | + | Status string `json:"status"` | |
| 956 | + | Domain string `json:"domain"` | |
| 957 | + | } | |
| 958 | + | assert.NoError(t, json.NewDecoder(rec.Body).Decode(&response)) | |
| 959 | + | assert.Equal(t, "restarted", response.Status) | |
| 960 | + | assert.Equal(t, "test.example.com", response.Domain) | |
| 961 | + | }) | |
| 962 | + | } | |
| 963 | + | } | |
| 964 | + | ||
| 918 | 965 | func TestHandler_Tags_InvalidRepository(t *testing.T) { | |
| 919 | 966 | configSvc := inmocks.NewMockConfigService(t) | |
| 920 | 967 | authSvc := inmocks.NewMockAuthService(t) | |
| @@ -945,6 +992,54 @@ func TestHandler_Tags_InvalidRepository(t *testing.T) { | |||
| 945 | 992 | } | |
| 946 | 993 | } | |
| 947 | 994 | ||
| 995 | + | func TestHandler_Tags_Success(t *testing.T) { | |
| 996 | + | configSvc := inmocks.NewMockConfigService(t) | |
| 997 | + | authSvc := inmocks.NewMockAuthService(t) | |
| 998 | + | containerSvc := inmocks.NewMockContainerService(t) | |
| 999 | + | secretSvc := inmocks.NewMockSecretService(t) | |
| 1000 | + | registrySvc := inmocks.NewMockRegistryService(t) | |
| 1001 | + | ||
| 1002 | + | handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger()) | |
| 1003 | + | ||
| 1004 | + | registrySvc.EXPECT().ListTags(mock.Anything, "myapp").Return([]string{"latest", "v1.0"}, nil) | |
| 1005 | + | ||
| 1006 | + | req := httptest.NewRequest("GET", "/admin/tags/myapp", nil) | |
| 1007 | + | req = req.WithContext(ctxWithScopes("admin:status:read")) | |
| 1008 | + | rec := httptest.NewRecorder() | |
| 1009 | + | ||
| 1010 | + | handler.ServeHTTP(rec, req) | |
| 1011 | + | ||
| 1012 | + | assert.Equal(t, http.StatusOK, rec.Code) | |
| 1013 | + | var response struct { | |
| 1014 | + | Repository string `json:"repository"` | |
| 1015 | + | Tags []string `json:"tags"` | |
| 1016 | + | } | |
| 1017 | + | assert.NoError(t, json.NewDecoder(rec.Body).Decode(&response)) | |
| 1018 | + | assert.Equal(t, "myapp", response.Repository) | |
| 1019 | + | assert.Equal(t, []string{"latest", "v1.0"}, response.Tags) | |
| 1020 | + | } | |
| 1021 | + | ||
| 1022 | + | func TestHandler_Tags_Error(t *testing.T) { | |
| 1023 | + | configSvc := inmocks.NewMockConfigService(t) | |
| 1024 | + | authSvc := inmocks.NewMockAuthService(t) | |
| 1025 | + | containerSvc := inmocks.NewMockContainerService(t) | |
| 1026 | + | secretSvc := inmocks.NewMockSecretService(t) | |
| 1027 | + | registrySvc := inmocks.NewMockRegistryService(t) | |
| 1028 | + | ||
| 1029 | + | handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger()) | |
| 1030 | + | ||
| 1031 | + | registrySvc.EXPECT().ListTags(mock.Anything, "myapp").Return(nil, fmt.Errorf("registry error")) | |
| 1032 | + | ||
| 1033 | + | req := httptest.NewRequest("GET", "/admin/tags/myapp", nil) | |
| 1034 | + | req = req.WithContext(ctxWithScopes("admin:status:read")) | |
| 1035 | + | rec := httptest.NewRecorder() | |
| 1036 | + | ||
| 1037 | + | handler.ServeHTTP(rec, req) | |
| 1038 | + | ||
| 1039 | + | assert.Equal(t, http.StatusInternalServerError, rec.Code) | |
| 1040 | + | assert.NotContains(t, rec.Body.String(), "registry error") | |
| 1041 | + | } | |
| 1042 | + | ||
| 948 | 1043 | func TestHandler_NotFound(t *testing.T) { | |
| 949 | 1044 | configSvc := inmocks.NewMockConfigService(t) | |
| 950 | 1045 | authSvc := inmocks.NewMockAuthService(t) | |
| @@ -915,6 +915,53 @@ func TestHandler_Restart_GenericError(t *testing.T) { | |||||
| 915 | assert.NotContains(t, rec.Body.String(), "secret details") | 915 | assert.NotContains(t, rec.Body.String(), "secret details") | ||
| 916 | } | 916 | } | ||
| 917 | 917 | ||||
| 918 | + | func TestHandler_Restart_Success(t *testing.T) { | |||
| 919 | + | configSvc := inmocks.NewMockConfigService(t) | |||
| 920 | + | authSvc := inmocks.NewMockAuthService(t) | |||
| 921 | + | containerSvc := inmocks.NewMockContainerService(t) | |||
| 922 | + | secretSvc := inmocks.NewMockSecretService(t) | |||
| 923 | + | ||||
| 924 | + | handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger()) | |||
| 925 | + | ||||
| 926 | + | tests := []struct { | |||
| 927 | + | name string | |||
| 928 | + | path string | |||
| 929 | + | withAttachments bool | |||
| 930 | + | }{ | |||
| 931 | + | { | |||
| 932 | + | name: "without attachments", | |||
| 933 | + | path: "/admin/restart/test.example.com", | |||
| 934 | + | withAttachments: false, | |||
| 935 | + | }, | |||
| 936 | + | { | |||
| 937 | + | name: "with attachments", | |||
| 938 | + | path: "/admin/restart/test.example.com?attachments=true", | |||
| 939 | + | withAttachments: true, | |||
| 940 | + | }, | |||
| 941 | + | } | |||
| 942 | + | ||||
| 943 | + | for _, tt := range tests { | |||
| 944 | + | t.Run(tt.name, func(t *testing.T) { | |||
| 945 | + | containerSvc.EXPECT().Restart(mock.Anything, "test.example.com", tt.withAttachments).Return(nil) | |||
| 946 | + | ||||
| 947 | + | req := httptest.NewRequest("POST", tt.path, nil) | |||
| 948 | + | req = req.WithContext(ctxWithScopes("admin:config:write")) | |||
| 949 | + | rec := httptest.NewRecorder() | |||
| 950 | + | ||||
| 951 | + | handler.ServeHTTP(rec, req) | |||
| 952 | + | ||||
| 953 | + | assert.Equal(t, http.StatusOK, rec.Code) | |||
| 954 | + | var response struct { | |||
| 955 | + | Status string `json:"status"` | |||
| 956 | + | Domain string `json:"domain"` | |||
| 957 | + | } | |||
| 958 | + | assert.NoError(t, json.NewDecoder(rec.Body).Decode(&response)) | |||
| 959 | + | assert.Equal(t, "restarted", response.Status) | |||
| 960 | + | assert.Equal(t, "test.example.com", response.Domain) | |||
| 961 | + | }) | |||
| 962 | + | } | |||
| 963 | + | } | |||
| 964 | + | ||||
| 918 | func TestHandler_Tags_InvalidRepository(t *testing.T) { | 965 | func TestHandler_Tags_InvalidRepository(t *testing.T) { | ||
| 919 | configSvc := inmocks.NewMockConfigService(t) | 966 | configSvc := inmocks.NewMockConfigService(t) | ||
| 920 | authSvc := inmocks.NewMockAuthService(t) | 967 | authSvc := inmocks.NewMockAuthService(t) | ||
| @@ -945,6 +992,54 @@ func TestHandler_Tags_InvalidRepository(t *testing.T) { | |||||
| 945 | } | 992 | } | ||
| 946 | } | 993 | } | ||
| 947 | 994 | ||||
| 995 | + | func TestHandler_Tags_Success(t *testing.T) { | |||
| 996 | + | configSvc := inmocks.NewMockConfigService(t) | |||
| 997 | + | authSvc := inmocks.NewMockAuthService(t) | |||
| 998 | + | containerSvc := inmocks.NewMockContainerService(t) | |||
| 999 | + | secretSvc := inmocks.NewMockSecretService(t) | |||
| 1000 | + | registrySvc := inmocks.NewMockRegistryService(t) | |||
| 1001 | + | ||||
| 1002 | + | handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger()) | |||
| 1003 | + | ||||
| 1004 | + | registrySvc.EXPECT().ListTags(mock.Anything, "myapp").Return([]string{"latest", "v1.0"}, nil) | |||
| 1005 | + | ||||
| 1006 | + | req := httptest.NewRequest("GET", "/admin/tags/myapp", nil) | |||
| 1007 | + | req = req.WithContext(ctxWithScopes("admin:status:read")) | |||
| 1008 | + | rec := httptest.NewRecorder() | |||
| 1009 | + | ||||
| 1010 | + | handler.ServeHTTP(rec, req) | |||
| 1011 | + | ||||
| 1012 | + | assert.Equal(t, http.StatusOK, rec.Code) | |||
| 1013 | + | var response struct { | |||
| 1014 | + | Repository string `json:"repository"` | |||
| 1015 | + | Tags []string `json:"tags"` | |||
| 1016 | + | } | |||
| 1017 | + | assert.NoError(t, json.NewDecoder(rec.Body).Decode(&response)) | |||
| 1018 | + | assert.Equal(t, "myapp", response.Repository) | |||
| 1019 | + | assert.Equal(t, []string{"latest", "v1.0"}, response.Tags) | |||
| 1020 | + | } | |||
| 1021 | + | ||||
| 1022 | + | func TestHandler_Tags_Error(t *testing.T) { | |||
| 1023 | + | configSvc := inmocks.NewMockConfigService(t) | |||
| 1024 | + | authSvc := inmocks.NewMockAuthService(t) | |||
| 1025 | + | containerSvc := inmocks.NewMockContainerService(t) | |||
| 1026 | + | secretSvc := inmocks.NewMockSecretService(t) | |||
| 1027 | + | registrySvc := inmocks.NewMockRegistryService(t) | |||
| 1028 | + | ||||
| 1029 | + | handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger()) | |||
| 1030 | + | ||||
| 1031 | + | registrySvc.EXPECT().ListTags(mock.Anything, "myapp").Return(nil, fmt.Errorf("registry error")) | |||
| 1032 | + | ||||
| 1033 | + | req := httptest.NewRequest("GET", "/admin/tags/myapp", nil) | |||
| 1034 | + | req = req.WithContext(ctxWithScopes("admin:status:read")) | |||
| 1035 | + | rec := httptest.NewRecorder() | |||
| 1036 | + | ||||
| 1037 | + | handler.ServeHTTP(rec, req) | |||
| 1038 | + | ||||
| 1039 | + | assert.Equal(t, http.StatusInternalServerError, rec.Code) | |||
| 1040 | + | assert.NotContains(t, rec.Body.String(), "registry error") | |||
| 1041 | + | } | |||
| 1042 | + | ||||
| 948 | func TestHandler_NotFound(t *testing.T) { | 1043 | func TestHandler_NotFound(t *testing.T) { | ||
| 949 | configSvc := inmocks.NewMockConfigService(t) | 1044 | configSvc := inmocks.NewMockConfigService(t) | ||
| 950 | authSvc := inmocks.NewMockAuthService(t) | 1045 | authSvc := inmocks.NewMockAuthService(t) | ||
M internal/usecase/container/service.go +6 -1
| @@ -223,7 +223,12 @@ func (s *Service) Restart(ctx context.Context, domainName string, withAttachment | |||
| 223 | 223 | ||
| 224 | 224 | s.mu.RLock() | |
| 225 | 225 | container, exists := s.containers[domainName] | |
| 226 | - | attachmentIDs := s.attachments[domainName] | |
| 226 | + | attachments := s.attachments[domainName] | |
| 227 | + | var attachmentIDs []string | |
| 228 | + | if len(attachments) > 0 { | |
| 229 | + | attachmentIDs = make([]string, len(attachments)) | |
| 230 | + | copy(attachmentIDs, attachments) | |
| 231 | + | } | |
| 227 | 232 | s.mu.RUnlock() | |
| 228 | 233 | ||
| 229 | 234 | if !exists || container == nil { | |
| @@ -223,7 +223,12 @@ func (s *Service) Restart(ctx context.Context, domainName string, withAttachment | |||||
| 223 | 223 | ||||
| 224 | s.mu.RLock() | 224 | s.mu.RLock() | ||
| 225 | container, exists := s.containers[domainName] | 225 | container, exists := s.containers[domainName] | ||
| 226 | - | attachmentIDs := s.attachments[domainName] | 226 | + | attachments := s.attachments[domainName] |
| 227 | + | var attachmentIDs []string | |||
| 228 | + | if len(attachments) > 0 { | |||
| 229 | + | attachmentIDs = make([]string, len(attachments)) | |||
| 230 | + | copy(attachmentIDs, attachments) | |||
| 231 | + | } | |||
| 227 | s.mu.RUnlock() | 232 | s.mu.RUnlock() | ||
| 228 | 233 | ||||
| 229 | if !exists || container == nil { | 234 | if !exists || container == nil { | ||
M internal/usecase/container/service_test.go +47 -0
| @@ -3,6 +3,7 @@ package container | |||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | 5 | "errors" | |
| 6 | + | "fmt" | |
| 6 | 7 | "testing" | |
| 7 | 8 | "time" | |
| 8 | 9 | ||
| @@ -601,6 +602,52 @@ func TestService_Restart_Success(t *testing.T) { | |||
| 601 | 602 | assert.NoError(t, err) | |
| 602 | 603 | } | |
| 603 | 604 | ||
| 605 | + | func TestService_Restart_Error(t *testing.T) { | |
| 606 | + | runtime := mocks.NewMockContainerRuntime(t) | |
| 607 | + | envLoader := mocks.NewMockEnvLoader(t) | |
| 608 | + | eventBus := mocks.NewMockEventPublisher(t) | |
| 609 | + | ||
| 610 | + | svc := NewService(runtime, envLoader, eventBus, nil, Config{}) | |
| 611 | + | ctx := testContext() | |
| 612 | + | ||
| 613 | + | svc.containers["test.example.com"] = &domain.Container{ | |
| 614 | + | ID: "container-123", | |
| 615 | + | Name: "gordon-test.example.com", | |
| 616 | + | Status: "running", | |
| 617 | + | } | |
| 618 | + | ||
| 619 | + | runtime.EXPECT().RestartContainer(mock.Anything, "container-123").Return(errors.New("restart failed")) | |
| 620 | + | ||
| 621 | + | err := svc.Restart(ctx, "test.example.com", false) | |
| 622 | + | ||
| 623 | + | assert.Error(t, err) | |
| 624 | + | assert.Contains(t, err.Error(), "failed to restart container") | |
| 625 | + | } | |
| 626 | + | ||
| 627 | + | func TestService_Restart_WithAttachments(t *testing.T) { | |
| 628 | + | runtime := mocks.NewMockContainerRuntime(t) | |
| 629 | + | envLoader := mocks.NewMockEnvLoader(t) | |
| 630 | + | eventBus := mocks.NewMockEventPublisher(t) | |
| 631 | + | ||
| 632 | + | svc := NewService(runtime, envLoader, eventBus, nil, Config{}) | |
| 633 | + | ctx := testContext() | |
| 634 | + | ||
| 635 | + | svc.containers["test.example.com"] = &domain.Container{ | |
| 636 | + | ID: "container-123", | |
| 637 | + | Name: "gordon-test.example.com", | |
| 638 | + | Status: "running", | |
| 639 | + | } | |
| 640 | + | svc.attachments["test.example.com"] = []string{"container-attach-1", "container-attach-2"} | |
| 641 | + | ||
| 642 | + | runtime.EXPECT().RestartContainer(mock.Anything, "container-123").Return(nil) | |
| 643 | + | runtime.EXPECT().RestartContainer(mock.Anything, "container-attach-1").Return(nil) | |
| 644 | + | runtime.EXPECT().RestartContainer(mock.Anything, "container-attach-2").Return(fmt.Errorf("boom")) | |
| 645 | + | ||
| 646 | + | err := svc.Restart(ctx, "test.example.com", true) | |
| 647 | + | ||
| 648 | + | assert.NoError(t, err) | |
| 649 | + | } | |
| 650 | + | ||
| 604 | 651 | func TestNormalizeImageRef(t *testing.T) { | |
| 605 | 652 | tests := []struct { | |
| 606 | 653 | name string | |
| @@ -3,6 +3,7 @@ package container | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | "errors" | 5 | "errors" | ||
| 6 | + | "fmt" | |||
| 6 | "testing" | 7 | "testing" | ||
| 7 | "time" | 8 | "time" | ||
| 8 | 9 | ||||
| @@ -601,6 +602,52 @@ func TestService_Restart_Success(t *testing.T) { | |||||
| 601 | assert.NoError(t, err) | 602 | assert.NoError(t, err) | ||
| 602 | } | 603 | } | ||
| 603 | 604 | ||||
| 605 | + | func TestService_Restart_Error(t *testing.T) { | |||
| 606 | + | runtime := mocks.NewMockContainerRuntime(t) | |||
| 607 | + | envLoader := mocks.NewMockEnvLoader(t) | |||
| 608 | + | eventBus := mocks.NewMockEventPublisher(t) | |||
| 609 | + | ||||
| 610 | + | svc := NewService(runtime, envLoader, eventBus, nil, Config{}) | |||
| 611 | + | ctx := testContext() | |||
| 612 | + | ||||
| 613 | + | svc.containers["test.example.com"] = &domain.Container{ | |||
| 614 | + | ID: "container-123", | |||
| 615 | + | Name: "gordon-test.example.com", | |||
| 616 | + | Status: "running", | |||
| 617 | + | } | |||
| 618 | + | ||||
| 619 | + | runtime.EXPECT().RestartContainer(mock.Anything, "container-123").Return(errors.New("restart failed")) | |||
| 620 | + | ||||
| 621 | + | err := svc.Restart(ctx, "test.example.com", false) | |||
| 622 | + | ||||
| 623 | + | assert.Error(t, err) | |||
| 624 | + | assert.Contains(t, err.Error(), "failed to restart container") | |||
| 625 | + | } | |||
| 626 | + | ||||
| 627 | + | func TestService_Restart_WithAttachments(t *testing.T) { | |||
| 628 | + | runtime := mocks.NewMockContainerRuntime(t) | |||
| 629 | + | envLoader := mocks.NewMockEnvLoader(t) | |||
| 630 | + | eventBus := mocks.NewMockEventPublisher(t) | |||
| 631 | + | ||||
| 632 | + | svc := NewService(runtime, envLoader, eventBus, nil, Config{}) | |||
| 633 | + | ctx := testContext() | |||
| 634 | + | ||||
| 635 | + | svc.containers["test.example.com"] = &domain.Container{ | |||
| 636 | + | ID: "container-123", | |||
| 637 | + | Name: "gordon-test.example.com", | |||
| 638 | + | Status: "running", | |||
| 639 | + | } | |||
| 640 | + | svc.attachments["test.example.com"] = []string{"container-attach-1", "container-attach-2"} | |||
| 641 | + | ||||
| 642 | + | runtime.EXPECT().RestartContainer(mock.Anything, "container-123").Return(nil) | |||
| 643 | + | runtime.EXPECT().RestartContainer(mock.Anything, "container-attach-1").Return(nil) | |||
| 644 | + | runtime.EXPECT().RestartContainer(mock.Anything, "container-attach-2").Return(fmt.Errorf("boom")) | |||
| 645 | + | ||||
| 646 | + | err := svc.Restart(ctx, "test.example.com", true) | |||
| 647 | + | ||||
| 648 | + | assert.NoError(t, err) | |||
| 649 | + | } | |||
| 650 | + | ||||
| 604 | func TestNormalizeImageRef(t *testing.T) { | 651 | func TestNormalizeImageRef(t *testing.T) { | ||
| 605 | tests := []struct { | 652 | tests := []struct { | ||
| 606 | name string | 653 | name string | ||
A internal/usecase/registry/errors.go +6 -0
| @@ -0,0 +1,6 @@ | |||
| 1 | + | package registry | |
| 2 | + | ||
| 3 | + | import "errors" | |
| 4 | + | ||
| 5 | + | // ErrRepositoryNotFound indicates a registry repository does not exist. | |
| 6 | + | var ErrRepositoryNotFound = errors.New("repository not found") | |
| @@ -0,0 +1,6 @@ | |||||
| 1 | + | package registry | |||
| 2 | + | ||||
| 3 | + | import "errors" | |||
| 4 | + | ||||
| 5 | + | // ErrRepositoryNotFound indicates a registry repository does not exist. | |||
| 6 | + | var ErrRepositoryNotFound = errors.New("repository not found") | |||