From eb1423616690fc96c2e4886275ffe0333256b4ae Mon Sep 17 00:00:00 2001 From: shankar0123 Date: Sat, 18 Apr 2026 00:46:58 +0000 Subject: [PATCH] M-2 PR-B: Collapse IssuerService + TargetService to ctx-first signatures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Delete bare TestConnection wrapper in IssuerService; rename TestConnectionWithContext → TestConnection - Delete TestTargetConnection delegate shim in TargetService (canonical TestConnection already ctx-first) - Add ctx first param to 10 handler-interface methods (ListIssuers/GetIssuer/CreateIssuer/UpdateIssuer/DeleteIssuer and ListTargets/GetTarget/CreateTarget/UpdateTarget/DeleteTarget) - Replace 16 context.Background() call sites with received ctx - Thread r.Context() through 12 HTTP handler sites in issuers.go and targets.go (outer TargetHandler.TestTargetConnection HTTP method name preserved for router compatibility) - Update MockIssuerService, MockTargetService, and mockTargetService (integration) for ctx-first forwarding; update test callsite literals Audit complete. Commit: 1f6cf0eafa665efb51de46d2f0a51968053f2c26. Sections: 12. Findings: 2/7/10/4/6. --- internal/api/handler/issuer_handler_test.go | 65 +++++++++++---------- internal/api/handler/issuers.go | 25 ++++---- internal/api/handler/target_handler_test.go | 61 +++++++++---------- internal/api/handler/targets.go | 25 ++++---- internal/integration/lifecycle_test.go | 22 +++---- internal/service/issuer.go | 35 +++++------ internal/service/issuer_test.go | 18 +++--- internal/service/target.go | 27 ++++----- internal/service/target_test.go | 18 ++++-- 9 files changed, 150 insertions(+), 146 deletions(-) diff --git a/internal/api/handler/issuer_handler_test.go b/internal/api/handler/issuer_handler_test.go index c600fa9..a3aea1d 100644 --- a/internal/api/handler/issuer_handler_test.go +++ b/internal/api/handler/issuer_handler_test.go @@ -2,6 +2,7 @@ package handler import ( "bytes" + "context" "encoding/json" "fmt" "net/http" @@ -15,52 +16,52 @@ import ( // MockIssuerService is a mock implementation of IssuerService interface. type MockIssuerService struct { - ListIssuersFn func(page, perPage int) ([]domain.Issuer, int64, error) - GetIssuerFn func(id string) (*domain.Issuer, error) - CreateIssuerFn func(issuer domain.Issuer) (*domain.Issuer, error) - UpdateIssuerFn func(id string, issuer domain.Issuer) (*domain.Issuer, error) - DeleteIssuerFn func(id string) error - TestConnectionFn func(id string) error + ListIssuersFn func(ctx context.Context, page, perPage int) ([]domain.Issuer, int64, error) + GetIssuerFn func(ctx context.Context, id string) (*domain.Issuer, error) + CreateIssuerFn func(ctx context.Context, issuer domain.Issuer) (*domain.Issuer, error) + UpdateIssuerFn func(ctx context.Context, id string, issuer domain.Issuer) (*domain.Issuer, error) + DeleteIssuerFn func(ctx context.Context, id string) error + TestConnectionFn func(ctx context.Context, id string) error } -func (m *MockIssuerService) ListIssuers(page, perPage int) ([]domain.Issuer, int64, error) { +func (m *MockIssuerService) ListIssuers(ctx context.Context, page, perPage int) ([]domain.Issuer, int64, error) { if m.ListIssuersFn != nil { - return m.ListIssuersFn(page, perPage) + return m.ListIssuersFn(ctx, page, perPage) } return nil, 0, nil } -func (m *MockIssuerService) GetIssuer(id string) (*domain.Issuer, error) { +func (m *MockIssuerService) GetIssuer(ctx context.Context, id string) (*domain.Issuer, error) { if m.GetIssuerFn != nil { - return m.GetIssuerFn(id) + return m.GetIssuerFn(ctx, id) } return nil, nil } -func (m *MockIssuerService) CreateIssuer(issuer domain.Issuer) (*domain.Issuer, error) { +func (m *MockIssuerService) CreateIssuer(ctx context.Context, issuer domain.Issuer) (*domain.Issuer, error) { if m.CreateIssuerFn != nil { - return m.CreateIssuerFn(issuer) + return m.CreateIssuerFn(ctx, issuer) } return nil, nil } -func (m *MockIssuerService) UpdateIssuer(id string, issuer domain.Issuer) (*domain.Issuer, error) { +func (m *MockIssuerService) UpdateIssuer(ctx context.Context, id string, issuer domain.Issuer) (*domain.Issuer, error) { if m.UpdateIssuerFn != nil { - return m.UpdateIssuerFn(id, issuer) + return m.UpdateIssuerFn(ctx, id, issuer) } return nil, nil } -func (m *MockIssuerService) DeleteIssuer(id string) error { +func (m *MockIssuerService) DeleteIssuer(ctx context.Context, id string) error { if m.DeleteIssuerFn != nil { - return m.DeleteIssuerFn(id) + return m.DeleteIssuerFn(ctx, id) } return nil } -func (m *MockIssuerService) TestConnection(id string) error { +func (m *MockIssuerService) TestConnection(ctx context.Context, id string) error { if m.TestConnectionFn != nil { - return m.TestConnectionFn(id) + return m.TestConnectionFn(ctx, id) } return nil } @@ -85,7 +86,7 @@ func TestListIssuers_Success(t *testing.T) { } mock := &MockIssuerService{ - ListIssuersFn: func(page, perPage int) ([]domain.Issuer, int64, error) { + ListIssuersFn: func(_ context.Context, page, perPage int) ([]domain.Issuer, int64, error) { return []domain.Issuer{iss1, iss2}, 2, nil }, } @@ -113,7 +114,7 @@ func TestListIssuers_Success(t *testing.T) { func TestListIssuers_Pagination(t *testing.T) { var capturedPage, capturedPerPage int mock := &MockIssuerService{ - ListIssuersFn: func(page, perPage int) ([]domain.Issuer, int64, error) { + ListIssuersFn: func(_ context.Context, page, perPage int) ([]domain.Issuer, int64, error) { capturedPage = page capturedPerPage = perPage return []domain.Issuer{}, 0, nil @@ -137,7 +138,7 @@ func TestListIssuers_Pagination(t *testing.T) { func TestListIssuers_ServiceError(t *testing.T) { mock := &MockIssuerService{ - ListIssuersFn: func(page, perPage int) ([]domain.Issuer, int64, error) { + ListIssuersFn: func(_ context.Context, page, perPage int) ([]domain.Issuer, int64, error) { return nil, 0, ErrMockServiceFailed }, } @@ -169,7 +170,7 @@ func TestListIssuers_MethodNotAllowed(t *testing.T) { func TestGetIssuer_Success(t *testing.T) { now := time.Now() mock := &MockIssuerService{ - GetIssuerFn: func(id string) (*domain.Issuer, error) { + GetIssuerFn: func(_ context.Context, id string) (*domain.Issuer, error) { return &domain.Issuer{ ID: id, Name: "Local CA", @@ -195,7 +196,7 @@ func TestGetIssuer_Success(t *testing.T) { func TestGetIssuer_NotFound(t *testing.T) { mock := &MockIssuerService{ - GetIssuerFn: func(id string) (*domain.Issuer, error) { + GetIssuerFn: func(_ context.Context, id string) (*domain.Issuer, error) { return nil, ErrMockNotFound }, } @@ -228,7 +229,7 @@ func TestGetIssuer_EmptyID(t *testing.T) { func TestCreateIssuer_Success(t *testing.T) { now := time.Now() mock := &MockIssuerService{ - CreateIssuerFn: func(issuer domain.Issuer) (*domain.Issuer, error) { + CreateIssuerFn: func(_ context.Context, issuer domain.Issuer) (*domain.Issuer, error) { issuer.ID = "iss-new" issuer.CreatedAt = now issuer.UpdatedAt = now @@ -328,7 +329,7 @@ func TestCreateIssuer_NameTooLong(t *testing.T) { func TestCreateIssuer_DuplicateName(t *testing.T) { mock := &MockIssuerService{ - CreateIssuerFn: func(issuer domain.Issuer) (*domain.Issuer, error) { + CreateIssuerFn: func(_ context.Context, issuer domain.Issuer) (*domain.Issuer, error) { return nil, fmt.Errorf("failed to create issuer: duplicate key value violates unique constraint \"issuers_name_key\"") }, } @@ -361,7 +362,7 @@ func TestCreateIssuer_DuplicateName(t *testing.T) { func TestCreateIssuer_UnsupportedType(t *testing.T) { mock := &MockIssuerService{ - CreateIssuerFn: func(issuer domain.Issuer) (*domain.Issuer, error) { + CreateIssuerFn: func(_ context.Context, issuer domain.Issuer) (*domain.Issuer, error) { return nil, fmt.Errorf("unsupported issuer type: FakeCA") }, } @@ -394,7 +395,7 @@ func TestCreateIssuer_UnsupportedType(t *testing.T) { func TestCreateIssuer_GenericServiceError(t *testing.T) { mock := &MockIssuerService{ - CreateIssuerFn: func(issuer domain.Issuer) (*domain.Issuer, error) { + CreateIssuerFn: func(_ context.Context, issuer domain.Issuer) (*domain.Issuer, error) { return nil, fmt.Errorf("failed to encrypt config: cipher error") }, } @@ -419,7 +420,7 @@ func TestCreateIssuer_GenericServiceError(t *testing.T) { func TestUpdateIssuer_DuplicateName(t *testing.T) { mock := &MockIssuerService{ - UpdateIssuerFn: func(id string, issuer domain.Issuer) (*domain.Issuer, error) { + UpdateIssuerFn: func(_ context.Context, id string, issuer domain.Issuer) (*domain.Issuer, error) { return nil, fmt.Errorf("failed to update issuer: duplicate key value violates unique constraint") }, } @@ -445,7 +446,7 @@ func TestUpdateIssuer_DuplicateName(t *testing.T) { func TestDeleteIssuer_Success(t *testing.T) { var deletedID string mock := &MockIssuerService{ - DeleteIssuerFn: func(id string) error { + DeleteIssuerFn: func(_ context.Context, id string) error { deletedID = id return nil }, @@ -468,7 +469,7 @@ func TestDeleteIssuer_Success(t *testing.T) { func TestDeleteIssuer_ServiceError(t *testing.T) { mock := &MockIssuerService{ - DeleteIssuerFn: func(id string) error { + DeleteIssuerFn: func(_ context.Context, id string) error { return ErrMockServiceFailed }, } @@ -487,7 +488,7 @@ func TestDeleteIssuer_ServiceError(t *testing.T) { func TestTestConnection_Success(t *testing.T) { mock := &MockIssuerService{ - TestConnectionFn: func(id string) error { + TestConnectionFn: func(_ context.Context, id string) error { return nil }, } @@ -514,7 +515,7 @@ func TestTestConnection_Success(t *testing.T) { func TestTestConnection_Failure(t *testing.T) { mock := &MockIssuerService{ - TestConnectionFn: func(id string) error { + TestConnectionFn: func(_ context.Context, id string) error { return ErrMockServiceFailed }, } diff --git a/internal/api/handler/issuers.go b/internal/api/handler/issuers.go index ad5a4e4..7ad9641 100644 --- a/internal/api/handler/issuers.go +++ b/internal/api/handler/issuers.go @@ -1,6 +1,7 @@ package handler import ( + "context" "encoding/json" "log/slog" "net/http" @@ -13,12 +14,12 @@ import ( // IssuerService defines the service interface for issuer operations. type IssuerService interface { - ListIssuers(page, perPage int) ([]domain.Issuer, int64, error) - GetIssuer(id string) (*domain.Issuer, error) - CreateIssuer(issuer domain.Issuer) (*domain.Issuer, error) - UpdateIssuer(id string, issuer domain.Issuer) (*domain.Issuer, error) - DeleteIssuer(id string) error - TestConnection(id string) error + ListIssuers(ctx context.Context, page, perPage int) ([]domain.Issuer, int64, error) + GetIssuer(ctx context.Context, id string) (*domain.Issuer, error) + CreateIssuer(ctx context.Context, issuer domain.Issuer) (*domain.Issuer, error) + UpdateIssuer(ctx context.Context, id string, issuer domain.Issuer) (*domain.Issuer, error) + DeleteIssuer(ctx context.Context, id string) error + TestConnection(ctx context.Context, id string) error } // IssuerHandler handles HTTP requests for issuer operations. @@ -61,7 +62,7 @@ func (h IssuerHandler) ListIssuers(w http.ResponseWriter, r *http.Request) { } } - issuers, total, err := h.svc.ListIssuers(page, perPage) + issuers, total, err := h.svc.ListIssuers(r.Context(), page, perPage) if err != nil { ErrorWithRequestID(w, http.StatusInternalServerError, "Failed to list issuers", requestID) return @@ -93,7 +94,7 @@ func (h IssuerHandler) GetIssuer(w http.ResponseWriter, r *http.Request) { return } - issuer, err := h.svc.GetIssuer(id) + issuer, err := h.svc.GetIssuer(r.Context(), id) if err != nil { ErrorWithRequestID(w, http.StatusNotFound, "Issuer not found", requestID) return @@ -132,7 +133,7 @@ func (h IssuerHandler) CreateIssuer(w http.ResponseWriter, r *http.Request) { return } - created, err := h.svc.CreateIssuer(issuer) + created, err := h.svc.CreateIssuer(r.Context(), issuer) if err != nil { h.logger.Error("failed to create issuer", "error", err, "name", issuer.Name, "type", issuer.Type) errMsg := err.Error() @@ -174,7 +175,7 @@ func (h IssuerHandler) UpdateIssuer(w http.ResponseWriter, r *http.Request) { return } - updated, err := h.svc.UpdateIssuer(id, issuer) + updated, err := h.svc.UpdateIssuer(r.Context(), id, issuer) if err != nil { h.logger.Error("failed to update issuer", "error", err, "id", id) errMsg := err.Error() @@ -208,7 +209,7 @@ func (h IssuerHandler) DeleteIssuer(w http.ResponseWriter, r *http.Request) { return } - if err := h.svc.DeleteIssuer(id); err != nil { + if err := h.svc.DeleteIssuer(r.Context(), id); err != nil { if strings.Contains(err.Error(), "violates foreign key") || strings.Contains(err.Error(), "RESTRICT") { ErrorWithRequestID(w, http.StatusConflict, "Cannot delete issuer: certificates are still using this issuer", requestID) } else if strings.Contains(err.Error(), "not found") { @@ -241,7 +242,7 @@ func (h IssuerHandler) TestConnection(w http.ResponseWriter, r *http.Request) { } issuerID := parts[0] - if err := h.svc.TestConnection(issuerID); err != nil { + if err := h.svc.TestConnection(r.Context(), issuerID); err != nil { ErrorWithRequestID(w, http.StatusInternalServerError, "Connection test failed", requestID) return } diff --git a/internal/api/handler/target_handler_test.go b/internal/api/handler/target_handler_test.go index 8a34e36..8d80f0c 100644 --- a/internal/api/handler/target_handler_test.go +++ b/internal/api/handler/target_handler_test.go @@ -2,6 +2,7 @@ package handler import ( "bytes" + "context" "encoding/json" "net/http" "net/http/httptest" @@ -13,52 +14,52 @@ import ( // MockTargetService is a mock implementation of TargetService interface. type MockTargetService struct { - ListTargetsFn func(page, perPage int) ([]domain.DeploymentTarget, int64, error) - GetTargetFn func(id string) (*domain.DeploymentTarget, error) - CreateTargetFn func(target domain.DeploymentTarget) (*domain.DeploymentTarget, error) - UpdateTargetFn func(id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) - DeleteTargetFn func(id string) error - TestTargetConnectionFn func(id string) error + ListTargetsFn func(ctx context.Context, page, perPage int) ([]domain.DeploymentTarget, int64, error) + GetTargetFn func(ctx context.Context, id string) (*domain.DeploymentTarget, error) + CreateTargetFn func(ctx context.Context, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) + UpdateTargetFn func(ctx context.Context, id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) + DeleteTargetFn func(ctx context.Context, id string) error + TestConnectionFn func(ctx context.Context, id string) error } -func (m *MockTargetService) ListTargets(page, perPage int) ([]domain.DeploymentTarget, int64, error) { +func (m *MockTargetService) ListTargets(ctx context.Context, page, perPage int) ([]domain.DeploymentTarget, int64, error) { if m.ListTargetsFn != nil { - return m.ListTargetsFn(page, perPage) + return m.ListTargetsFn(ctx, page, perPage) } return nil, 0, nil } -func (m *MockTargetService) GetTarget(id string) (*domain.DeploymentTarget, error) { +func (m *MockTargetService) GetTarget(ctx context.Context, id string) (*domain.DeploymentTarget, error) { if m.GetTargetFn != nil { - return m.GetTargetFn(id) + return m.GetTargetFn(ctx, id) } return nil, nil } -func (m *MockTargetService) CreateTarget(target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { +func (m *MockTargetService) CreateTarget(ctx context.Context, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { if m.CreateTargetFn != nil { - return m.CreateTargetFn(target) + return m.CreateTargetFn(ctx, target) } return nil, nil } -func (m *MockTargetService) UpdateTarget(id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { +func (m *MockTargetService) UpdateTarget(ctx context.Context, id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { if m.UpdateTargetFn != nil { - return m.UpdateTargetFn(id, target) + return m.UpdateTargetFn(ctx, id, target) } return nil, nil } -func (m *MockTargetService) DeleteTarget(id string) error { +func (m *MockTargetService) DeleteTarget(ctx context.Context, id string) error { if m.DeleteTargetFn != nil { - return m.DeleteTargetFn(id) + return m.DeleteTargetFn(ctx, id) } return nil } -func (m *MockTargetService) TestTargetConnection(id string) error { - if m.TestTargetConnectionFn != nil { - return m.TestTargetConnectionFn(id) +func (m *MockTargetService) TestConnection(ctx context.Context, id string) error { + if m.TestConnectionFn != nil { + return m.TestConnectionFn(ctx, id) } return nil } @@ -85,7 +86,7 @@ func TestListTargets_Success(t *testing.T) { } mock := &MockTargetService{ - ListTargetsFn: func(page, perPage int) ([]domain.DeploymentTarget, int64, error) { + ListTargetsFn: func(_ context.Context, page, perPage int) ([]domain.DeploymentTarget, int64, error) { return []domain.DeploymentTarget{t1, t2}, 2, nil }, } @@ -113,7 +114,7 @@ func TestListTargets_Success(t *testing.T) { func TestListTargets_Pagination(t *testing.T) { var capturedPage, capturedPerPage int mock := &MockTargetService{ - ListTargetsFn: func(page, perPage int) ([]domain.DeploymentTarget, int64, error) { + ListTargetsFn: func(_ context.Context, page, perPage int) ([]domain.DeploymentTarget, int64, error) { capturedPage = page capturedPerPage = perPage return []domain.DeploymentTarget{}, 0, nil @@ -137,7 +138,7 @@ func TestListTargets_Pagination(t *testing.T) { func TestListTargets_ServiceError(t *testing.T) { mock := &MockTargetService{ - ListTargetsFn: func(page, perPage int) ([]domain.DeploymentTarget, int64, error) { + ListTargetsFn: func(_ context.Context, page, perPage int) ([]domain.DeploymentTarget, int64, error) { return nil, 0, ErrMockServiceFailed }, } @@ -169,7 +170,7 @@ func TestListTargets_MethodNotAllowed(t *testing.T) { func TestGetTarget_Success(t *testing.T) { now := time.Now() mock := &MockTargetService{ - GetTargetFn: func(id string) (*domain.DeploymentTarget, error) { + GetTargetFn: func(_ context.Context, id string) (*domain.DeploymentTarget, error) { return &domain.DeploymentTarget{ ID: id, Name: "NGINX Proxy", @@ -196,7 +197,7 @@ func TestGetTarget_Success(t *testing.T) { func TestGetTarget_NotFound(t *testing.T) { mock := &MockTargetService{ - GetTargetFn: func(id string) (*domain.DeploymentTarget, error) { + GetTargetFn: func(_ context.Context, id string) (*domain.DeploymentTarget, error) { return nil, ErrMockNotFound }, } @@ -229,7 +230,7 @@ func TestGetTarget_EmptyID(t *testing.T) { func TestCreateTarget_Success(t *testing.T) { now := time.Now() mock := &MockTargetService{ - CreateTargetFn: func(target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { + CreateTargetFn: func(_ context.Context, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { target.ID = "t-new" target.CreatedAt = now target.UpdatedAt = now @@ -342,7 +343,7 @@ func TestCreateTarget_MethodNotAllowed(t *testing.T) { func TestUpdateTarget_Success(t *testing.T) { now := time.Now() mock := &MockTargetService{ - UpdateTargetFn: func(id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { + UpdateTargetFn: func(_ context.Context, id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { return &domain.DeploymentTarget{ ID: id, Name: target.Name, @@ -375,7 +376,7 @@ func TestUpdateTarget_Success(t *testing.T) { func TestDeleteTarget_Success(t *testing.T) { var deletedID string mock := &MockTargetService{ - DeleteTargetFn: func(id string) error { + DeleteTargetFn: func(_ context.Context, id string) error { deletedID = id return nil }, @@ -398,7 +399,7 @@ func TestDeleteTarget_Success(t *testing.T) { func TestDeleteTarget_ServiceError(t *testing.T) { mock := &MockTargetService{ - DeleteTargetFn: func(id string) error { + DeleteTargetFn: func(_ context.Context, id string) error { return ErrMockServiceFailed }, } @@ -430,7 +431,7 @@ func TestDeleteTarget_EmptyID(t *testing.T) { func TestTestTargetConnection_Success(t *testing.T) { mock := &MockTargetService{ - TestTargetConnectionFn: func(id string) error { + TestConnectionFn: func(_ context.Context, id string) error { return nil }, } @@ -457,7 +458,7 @@ func TestTestTargetConnection_Success(t *testing.T) { func TestTestTargetConnection_Failed(t *testing.T) { mock := &MockTargetService{ - TestTargetConnectionFn: func(id string) error { + TestConnectionFn: func(_ context.Context, id string) error { return ErrMockServiceFailed }, } diff --git a/internal/api/handler/targets.go b/internal/api/handler/targets.go index 84578a8..a6e23db 100644 --- a/internal/api/handler/targets.go +++ b/internal/api/handler/targets.go @@ -1,6 +1,7 @@ package handler import ( + "context" "encoding/json" "net/http" "strconv" @@ -12,12 +13,12 @@ import ( // TargetService defines the service interface for deployment target operations. type TargetService interface { - ListTargets(page, perPage int) ([]domain.DeploymentTarget, int64, error) - GetTarget(id string) (*domain.DeploymentTarget, error) - CreateTarget(target domain.DeploymentTarget) (*domain.DeploymentTarget, error) - UpdateTarget(id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) - DeleteTarget(id string) error - TestTargetConnection(id string) error + ListTargets(ctx context.Context, page, perPage int) ([]domain.DeploymentTarget, int64, error) + GetTarget(ctx context.Context, id string) (*domain.DeploymentTarget, error) + CreateTarget(ctx context.Context, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) + UpdateTarget(ctx context.Context, id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) + DeleteTarget(ctx context.Context, id string) error + TestConnection(ctx context.Context, id string) error } // TargetHandler handles HTTP requests for deployment target operations. @@ -54,7 +55,7 @@ func (h TargetHandler) ListTargets(w http.ResponseWriter, r *http.Request) { } } - targets, total, err := h.svc.ListTargets(page, perPage) + targets, total, err := h.svc.ListTargets(r.Context(), page, perPage) if err != nil { ErrorWithRequestID(w, http.StatusInternalServerError, "Failed to list targets", requestID) return @@ -86,7 +87,7 @@ func (h TargetHandler) GetTarget(w http.ResponseWriter, r *http.Request) { return } - target, err := h.svc.GetTarget(id) + target, err := h.svc.GetTarget(r.Context(), id) if err != nil { ErrorWithRequestID(w, http.StatusNotFound, "Target not found", requestID) return @@ -125,7 +126,7 @@ func (h TargetHandler) CreateTarget(w http.ResponseWriter, r *http.Request) { return } - created, err := h.svc.CreateTarget(target) + created, err := h.svc.CreateTarget(r.Context(), target) if err != nil { ErrorWithRequestID(w, http.StatusInternalServerError, "Failed to create target", requestID) return @@ -158,7 +159,7 @@ func (h TargetHandler) UpdateTarget(w http.ResponseWriter, r *http.Request) { return } - updated, err := h.svc.UpdateTarget(id, target) + updated, err := h.svc.UpdateTarget(r.Context(), id, target) if err != nil { ErrorWithRequestID(w, http.StatusInternalServerError, "Failed to update target", requestID) return @@ -183,7 +184,7 @@ func (h TargetHandler) DeleteTarget(w http.ResponseWriter, r *http.Request) { return } - if err := h.svc.DeleteTarget(id); err != nil { + if err := h.svc.DeleteTarget(r.Context(), id); err != nil { ErrorWithRequestID(w, http.StatusInternalServerError, "Failed to delete target", requestID) return } @@ -210,7 +211,7 @@ func (h TargetHandler) TestTargetConnection(w http.ResponseWriter, r *http.Reque } id := parts[0] - if err := h.svc.TestTargetConnection(id); err != nil { + if err := h.svc.TestConnection(r.Context(), id); err != nil { JSON(w, http.StatusOK, map[string]interface{}{ "status": "failed", "message": err.Error(), diff --git a/internal/integration/lifecycle_test.go b/internal/integration/lifecycle_test.go index b6b7137..f406ed5 100644 --- a/internal/integration/lifecycle_test.go +++ b/internal/integration/lifecycle_test.go @@ -1036,8 +1036,8 @@ type mockTargetService struct { auditService *service.AuditService } -func (m *mockTargetService) ListTargets(page, perPage int) ([]domain.DeploymentTarget, int64, error) { - targets, err := m.targetRepo.List(context.Background()) +func (m *mockTargetService) ListTargets(ctx context.Context, page, perPage int) ([]domain.DeploymentTarget, int64, error) { + targets, err := m.targetRepo.List(ctx) if err != nil { return nil, 0, err } @@ -1048,30 +1048,30 @@ func (m *mockTargetService) ListTargets(page, perPage int) ([]domain.DeploymentT return result, int64(len(result)), nil } -func (m *mockTargetService) GetTarget(id string) (*domain.DeploymentTarget, error) { - return m.targetRepo.Get(context.Background(), id) +func (m *mockTargetService) GetTarget(ctx context.Context, id string) (*domain.DeploymentTarget, error) { + return m.targetRepo.Get(ctx, id) } -func (m *mockTargetService) CreateTarget(target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { - if err := m.targetRepo.Create(context.Background(), &target); err != nil { +func (m *mockTargetService) CreateTarget(ctx context.Context, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { + if err := m.targetRepo.Create(ctx, &target); err != nil { return nil, err } return &target, nil } -func (m *mockTargetService) UpdateTarget(id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { +func (m *mockTargetService) UpdateTarget(ctx context.Context, id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { target.ID = id - if err := m.targetRepo.Update(context.Background(), &target); err != nil { + if err := m.targetRepo.Update(ctx, &target); err != nil { return nil, err } return &target, nil } -func (m *mockTargetService) DeleteTarget(id string) error { - return m.targetRepo.Delete(context.Background(), id) +func (m *mockTargetService) DeleteTarget(ctx context.Context, id string) error { + return m.targetRepo.Delete(ctx, id) } -func (m *mockTargetService) TestTargetConnection(id string) error { +func (m *mockTargetService) TestConnection(ctx context.Context, id string) error { return nil // No-op for integration tests } diff --git a/internal/service/issuer.go b/internal/service/issuer.go index af18c17..2236da5 100644 --- a/internal/service/issuer.go +++ b/internal/service/issuer.go @@ -260,9 +260,9 @@ func (s *IssuerService) Delete(ctx context.Context, id string, actor string) err return nil } -// TestConnectionWithContext tests the connection to an issuer by instantiating a throwaway +// TestConnection tests the connection to an issuer by instantiating a throwaway // connector and calling ValidateConfig. Records the result in the database. -func (s *IssuerService) TestConnectionWithContext(ctx context.Context, id string) error { +func (s *IssuerService) TestConnection(ctx context.Context, id string) error { iss, err := s.issuerRepo.Get(ctx, id) if err != nil { return fmt.Errorf("issuer not found: %w", err) @@ -291,11 +291,6 @@ func (s *IssuerService) TestConnectionWithContext(ctx context.Context, id string return nil } -// TestConnection verifies the issuer connection (handler interface method). -func (s *IssuerService) TestConnection(id string) error { - return s.TestConnectionWithContext(context.Background(), id) -} - // BuildRegistry loads all enabled issuers from the database and rebuilds the dynamic registry. // Called at server startup. Partial failures (individual issuers failing to load) are logged // as warnings but don't prevent the server from starting. @@ -633,7 +628,7 @@ func (s *IssuerService) buildEnvVarSeeds(cfg *config.Config) []*domain.Issuer { } // ListIssuers returns paginated issuers (handler interface method). -func (s *IssuerService) ListIssuers(page, perPage int) ([]domain.Issuer, int64, error) { +func (s *IssuerService) ListIssuers(ctx context.Context, page, perPage int) ([]domain.Issuer, int64, error) { if page < 1 { page = 1 } @@ -641,7 +636,7 @@ func (s *IssuerService) ListIssuers(page, perPage int) ([]domain.Issuer, int64, perPage = 50 } - issuers, err := s.issuerRepo.List(context.Background()) + issuers, err := s.issuerRepo.List(ctx) if err != nil { return nil, 0, fmt.Errorf("failed to list issuers: %w", err) } @@ -658,12 +653,12 @@ func (s *IssuerService) ListIssuers(page, perPage int) ([]domain.Issuer, int64, } // GetIssuer returns a single issuer (handler interface method). -func (s *IssuerService) GetIssuer(id string) (*domain.Issuer, error) { - return s.issuerRepo.Get(context.Background(), id) +func (s *IssuerService) GetIssuer(ctx context.Context, id string) (*domain.Issuer, error) { + return s.issuerRepo.Get(ctx, id) } // CreateIssuer creates a new issuer (handler interface method). -func (s *IssuerService) CreateIssuer(iss domain.Issuer) (*domain.Issuer, error) { +func (s *IssuerService) CreateIssuer(ctx context.Context, iss domain.Issuer) (*domain.Issuer, error) { iss.Type = normalizeIssuerType(iss.Type) if !isValidIssuerType(iss.Type) { return nil, fmt.Errorf("unsupported issuer type: %s", iss.Type) @@ -700,26 +695,26 @@ func (s *IssuerService) CreateIssuer(iss domain.Issuer) (*domain.Issuer, error) iss.Config = redactConfigJSON(iss.Config) } - if err := s.issuerRepo.Create(context.Background(), &iss); err != nil { + if err := s.issuerRepo.Create(ctx, &iss); err != nil { return nil, fmt.Errorf("failed to create issuer: %w", err) } // Rebuild registry if iss.Enabled { - s.rebuildRegistryQuiet(context.Background()) + s.rebuildRegistryQuiet(ctx) } return &iss, nil } // UpdateIssuer modifies an issuer (handler interface method). -func (s *IssuerService) UpdateIssuer(id string, iss domain.Issuer) (*domain.Issuer, error) { +func (s *IssuerService) UpdateIssuer(ctx context.Context, id string, iss domain.Issuer) (*domain.Issuer, error) { iss.ID = id iss.UpdatedAt = time.Now() // Merge redacted fields with existing config if len(iss.Config) > 0 { - mergedConfig, err := s.mergeRedactedConfig(context.Background(), id, iss.Config) + mergedConfig, err := s.mergeRedactedConfig(ctx, id, iss.Config) if err != nil { return nil, fmt.Errorf("failed to merge config: %w", err) } @@ -732,18 +727,18 @@ func (s *IssuerService) UpdateIssuer(id string, iss domain.Issuer) (*domain.Issu iss.Config = redactConfigJSON(json.RawMessage(mergedConfig)) } - if err := s.issuerRepo.Update(context.Background(), &iss); err != nil { + if err := s.issuerRepo.Update(ctx, &iss); err != nil { return nil, fmt.Errorf("failed to update issuer: %w", err) } - s.rebuildRegistryQuiet(context.Background()) + s.rebuildRegistryQuiet(ctx) return &iss, nil } // DeleteIssuer removes an issuer (handler interface method). -func (s *IssuerService) DeleteIssuer(id string) error { - if err := s.issuerRepo.Delete(context.Background(), id); err != nil { +func (s *IssuerService) DeleteIssuer(ctx context.Context, id string) error { + if err := s.issuerRepo.Delete(ctx, id); err != nil { return err } if s.registry != nil { diff --git a/internal/service/issuer_test.go b/internal/service/issuer_test.go index ad4d96e..813a209 100644 --- a/internal/service/issuer_test.go +++ b/internal/service/issuer_test.go @@ -484,10 +484,10 @@ func TestIssuerService_TestConnection_Success(t *testing.T) { svc := NewIssuerService(repo, auditService, NewIssuerRegistry(slog.Default()), "", slog.Default()) - err := svc.TestConnectionWithContext(ctx, "iss-test-conn") + err := svc.TestConnection(ctx, "iss-test-conn") if err != nil { - t.Fatalf("TestConnectionWithContext failed: %v", err) + t.Fatalf("TestConnection failed: %v", err) } } @@ -502,7 +502,7 @@ func TestIssuerService_TestConnection_NotFound(t *testing.T) { registry := NewIssuerRegistry(slog.Default()) service := NewIssuerService(repo, auditService, registry, "", slog.Default()) - err := service.TestConnectionWithContext(ctx, "nonexistent-issuer") + err := service.TestConnection(ctx, "nonexistent-issuer") if err == nil { t.Fatal("expected error for nonexistent issuer") @@ -542,7 +542,8 @@ func TestIssuerService_ListIssuers_HandlerInterface(t *testing.T) { service := NewIssuerService(repo, auditService, NewIssuerRegistry(slog.Default()), "", slog.Default()) - issuers, total, err := service.ListIssuers(1, 50) + ctx := context.Background() + issuers, total, err := service.ListIssuers(ctx, 1, 50) if err != nil { t.Fatalf("ListIssuers failed: %v", err) @@ -580,7 +581,8 @@ func TestIssuerService_CreateIssuer_HandlerInterface(t *testing.T) { Enabled: true, } - result, err := service.CreateIssuer(issuer) + ctx := context.Background() + result, err := service.CreateIssuer(ctx, issuer) if err != nil { t.Fatalf("CreateIssuer failed: %v", err) @@ -608,7 +610,8 @@ func TestIssuerService_DeleteIssuer_HandlerInterface(t *testing.T) { registry := NewIssuerRegistry(slog.Default()) service := NewIssuerService(repo, auditService, registry, "", slog.Default()) - err := service.DeleteIssuer("iss-handler-delete") + ctx := context.Background() + err := service.DeleteIssuer(ctx, "iss-handler-delete") if err != nil { t.Fatalf("DeleteIssuer failed: %v", err) @@ -722,7 +725,8 @@ func TestIssuerService_CreateIssuer_LowercaseType(t *testing.T) { Enabled: true, } - result, err := service.CreateIssuer(issuer) + ctx := context.Background() + result, err := service.CreateIssuer(ctx, issuer) if err != nil { t.Fatalf("CreateIssuer with lowercase 'stepca' should succeed, got: %v", err) } diff --git a/internal/service/target.go b/internal/service/target.go index 1828203..0b7f464 100644 --- a/internal/service/target.go +++ b/internal/service/target.go @@ -242,7 +242,7 @@ func (s *TargetService) TestConnection(ctx context.Context, id string) error { } // ListTargets returns paginated targets (handler interface method). -func (s *TargetService) ListTargets(page, perPage int) ([]domain.DeploymentTarget, int64, error) { +func (s *TargetService) ListTargets(ctx context.Context, page, perPage int) ([]domain.DeploymentTarget, int64, error) { if page < 1 { page = 1 } @@ -250,7 +250,7 @@ func (s *TargetService) ListTargets(page, perPage int) ([]domain.DeploymentTarge perPage = 50 } - targets, err := s.targetRepo.List(context.Background()) + targets, err := s.targetRepo.List(ctx) if err != nil { return nil, 0, fmt.Errorf("failed to list targets: %w", err) } @@ -267,12 +267,12 @@ func (s *TargetService) ListTargets(page, perPage int) ([]domain.DeploymentTarge } // GetTarget returns a single target (handler interface method). -func (s *TargetService) GetTarget(id string) (*domain.DeploymentTarget, error) { - return s.targetRepo.Get(context.Background(), id) +func (s *TargetService) GetTarget(ctx context.Context, id string) (*domain.DeploymentTarget, error) { + return s.targetRepo.Get(ctx, id) } // CreateTarget creates a new target (handler interface method). -func (s *TargetService) CreateTarget(target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { +func (s *TargetService) CreateTarget(ctx context.Context, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { if !isValidTargetType(target.Type) { return nil, fmt.Errorf("unsupported target type: %s", target.Type) } @@ -308,20 +308,20 @@ func (s *TargetService) CreateTarget(target domain.DeploymentTarget) (*domain.De target.Config = redactConfigJSON(target.Config) } - if err := s.targetRepo.Create(context.Background(), &target); err != nil { + if err := s.targetRepo.Create(ctx, &target); err != nil { return nil, fmt.Errorf("failed to create target: %w", err) } return &target, nil } // UpdateTarget modifies a target (handler interface method). -func (s *TargetService) UpdateTarget(id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { +func (s *TargetService) UpdateTarget(ctx context.Context, id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) { target.ID = id target.UpdatedAt = time.Now() // Merge redacted fields with existing config if len(target.Config) > 0 { - mergedConfig, err := s.mergeRedactedConfig(context.Background(), id, target.Config) + mergedConfig, err := s.mergeRedactedConfig(ctx, id, target.Config) if err != nil { return nil, fmt.Errorf("failed to merge config: %w", err) } @@ -334,20 +334,15 @@ func (s *TargetService) UpdateTarget(id string, target domain.DeploymentTarget) target.Config = redactConfigJSON(json.RawMessage(mergedConfig)) } - if err := s.targetRepo.Update(context.Background(), &target); err != nil { + if err := s.targetRepo.Update(ctx, &target); err != nil { return nil, fmt.Errorf("failed to update target: %w", err) } return &target, nil } // DeleteTarget removes a target (handler interface method). -func (s *TargetService) DeleteTarget(id string) error { - return s.targetRepo.Delete(context.Background(), id) -} - -// TestTargetConnection tests target connectivity (handler interface method). -func (s *TargetService) TestTargetConnection(id string) error { - return s.TestConnection(context.Background(), id) +func (s *TargetService) DeleteTarget(ctx context.Context, id string) error { + return s.targetRepo.Delete(ctx, id) } // --- Internal helpers --- diff --git a/internal/service/target_test.go b/internal/service/target_test.go index d0a9677..21e473e 100644 --- a/internal/service/target_test.go +++ b/internal/service/target_test.go @@ -344,7 +344,8 @@ func TestTargetService_ListTargets_Success(t *testing.T) { targetRepo.AddTarget(target2) // Call handler-interface method - targets, total, err := svc.ListTargets(1, 50) + ctx := context.Background() + targets, total, err := svc.ListTargets(ctx, 1, 50) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -364,7 +365,8 @@ func TestTargetService_GetTarget_Success(t *testing.T) { target := &domain.DeploymentTarget{ID: "t-1", Name: "Target 1", Type: domain.TargetTypeNGINX} targetRepo.AddTarget(target) - result, err := svc.GetTarget("t-1") + ctx := context.Background() + result, err := svc.GetTarget(ctx, "t-1") if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -382,7 +384,8 @@ func TestTargetService_CreateTarget_Success(t *testing.T) { Type: domain.TargetTypeNGINX, } - result, err := svc.CreateTarget(target) + ctx := context.Background() + result, err := svc.CreateTarget(ctx, target) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -405,7 +408,8 @@ func TestTargetService_CreateTarget_InvalidType(t *testing.T) { Type: domain.TargetType("Unknown"), } - _, err := svc.CreateTarget(target) + ctx := context.Background() + _, err := svc.CreateTarget(ctx, target) if err == nil { t.Fatalf("expected error for invalid type, got nil") } @@ -424,7 +428,8 @@ func TestTargetService_UpdateTarget_Success(t *testing.T) { Type: domain.TargetTypeApache, } - result, err := svc.UpdateTarget("t-1", updated) + ctx := context.Background() + result, err := svc.UpdateTarget(ctx, "t-1", updated) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -442,7 +447,8 @@ func TestTargetService_DeleteTarget_Success(t *testing.T) { targetRepo.AddTarget(target) // Delete it - err := svc.DeleteTarget("t-1") + ctx := context.Background() + err := svc.DeleteTarget(ctx, "t-1") if err != nil { t.Fatalf("unexpected error: %v", err) }