From f462f8f50e312ea082517affd51fcaf1239027f8 Mon Sep 17 00:00:00 2001 From: Allisson Azevedo Date: Mon, 9 Mar 2026 14:48:59 -0300 Subject: [PATCH] feat(transit): change transit key deletion to use name instead of UUID This change aligns the transit key deletion endpoint with the rest of the Transit Engine API, which uses the key name as the primary identifier rather than a UUID. - Updated TransitKeyRepository and TransitKeyUseCase interfaces to change the Delete signature from uuid.UUID to string (name). - Implemented soft-delete logic in PostgreSQL and MySQL repositories to mark all versions of a transit key as deleted by name. - Updated TransitKeyHandler and route registration to use DELETE /v1/transit/keys/:name. - Refactored unit, repository, and integration tests to support name-based deletion and verified that all versions are correctly soft-deleted. - Updated OpenAPI specification, Engine documentation, and Authorization policies to reflect the new endpoint signature. - Cleaned up unused UUID imports and variables across the transit module and test suites. --- .../index.md | 5 + .../metadata.json | 8 ++ .../plan.md | 24 ++++ .../spec.md | 28 +++++ docs/auth/policies.md | 2 +- docs/concepts/api-fundamentals.md | 4 +- docs/openapi.yaml | 48 ++++---- internal/http/server.go | 2 +- internal/transit/domain/repository.go | 4 +- internal/transit/http/transit_key_handler.go | 21 ++-- .../transit/http/transit_key_handler_test.go | 34 ++---- .../mysql/mysql_transit_key_repository.go | 15 +-- .../mysql_transit_key_repository_test.go | 103 +++++++++--------- .../postgresql_transit_key_repository.go | 10 +- .../postgresql_transit_key_repository_test.go | 96 ++++++++-------- internal/transit/usecase/interface.go | 6 +- internal/transit/usecase/metrics_decorator.go | 6 +- .../transit/usecase/metrics_decorator_test.go | 10 +- internal/transit/usecase/mocks/mocks.go | 44 ++++---- .../transit/usecase/transit_key_usecase.go | 6 +- .../usecase/transit_key_usecase_test.go | 12 +- test/integration/additional_scenarios_test.go | 11 +- test/integration/transit_flow_test.go | 11 +- 23 files changed, 263 insertions(+), 247 deletions(-) create mode 100644 conductor/archive/fix_transit_key_delete_inconsistency_20260309/index.md create mode 100644 conductor/archive/fix_transit_key_delete_inconsistency_20260309/metadata.json create mode 100644 conductor/archive/fix_transit_key_delete_inconsistency_20260309/plan.md create mode 100644 conductor/archive/fix_transit_key_delete_inconsistency_20260309/spec.md diff --git a/conductor/archive/fix_transit_key_delete_inconsistency_20260309/index.md b/conductor/archive/fix_transit_key_delete_inconsistency_20260309/index.md new file mode 100644 index 0000000..ecb8c1b --- /dev/null +++ b/conductor/archive/fix_transit_key_delete_inconsistency_20260309/index.md @@ -0,0 +1,5 @@ +# Track fix_transit_key_delete_inconsistency_20260309 Context + +- [Specification](./spec.md) +- [Implementation Plan](./plan.md) +- [Metadata](./metadata.json) diff --git a/conductor/archive/fix_transit_key_delete_inconsistency_20260309/metadata.json b/conductor/archive/fix_transit_key_delete_inconsistency_20260309/metadata.json new file mode 100644 index 0000000..8eabe0a --- /dev/null +++ b/conductor/archive/fix_transit_key_delete_inconsistency_20260309/metadata.json @@ -0,0 +1,8 @@ +{ + "track_id": "fix_transit_key_delete_inconsistency_20260309", + "type": "bug", + "status": "new", + "created_at": "2026-03-09T12:00:00Z", + "updated_at": "2026-03-09T12:00:00Z", + "description": "Fix Transit Key Delete Endpoint Inconsistency (DELETE /v1/transit/keys/:id` uses UUID while all other transit endpoints use `:name`. Make it consistent)" +} diff --git a/conductor/archive/fix_transit_key_delete_inconsistency_20260309/plan.md b/conductor/archive/fix_transit_key_delete_inconsistency_20260309/plan.md new file mode 100644 index 0000000..386da00 --- /dev/null +++ b/conductor/archive/fix_transit_key_delete_inconsistency_20260309/plan.md @@ -0,0 +1,24 @@ +# Implementation Plan: Fix Transit Key Delete Endpoint Inconsistency + +## Phase 1: Core Domain and Repository Updates +- [x] Task: Update `TransitKeyRepository` interface in `internal/transit/domain/repository.go` to change `Delete` signature from `transitKeyID uuid.UUID` to `name string`. +- [x] Task: Update `TransitKeyUseCase` interface in `internal/transit/usecase/interface.go` to change `Delete` signature from `transitKeyID uuid.UUID` to `name string`. +- [x] Task: Update PostgreSQL implementation in `internal/transit/repository/postgresql/postgresql_transit_key_repository.go` to soft-delete all versions by name. +- [x] Task: Update MySQL implementation in `internal/transit/repository/mysql/mysql_transit_key_repository.go` to soft-delete all versions by name. +- [x] Task: Update `TransitKeyUseCase` implementation in `internal/transit/usecase/transit_key_usecase.go` to use the new repository method. +- [x] Task: Update use case unit tests in `internal/transit/usecase/transit_key_usecase_test.go`. +- [x] Task: Update repository unit tests for both PostgreSQL and MySQL. +- [x] Task: Conductor - User Manual Verification 'Phase 1: Core Domain and Repository Updates' (Protocol in workflow.md) + +## Phase 2: HTTP Handler and Routing Updates +- [x] Task: Update `TransitKeyHandler.DeleteHandler` in `internal/transit/http/transit_key_handler.go` to extract `name` from URL parameters instead of `id` (UUID). +- [x] Task: Update route registration in `internal/http/server.go` to change `DELETE /v1/transit/keys/:id` to `DELETE /v1/transit/keys/:name`. +- [x] Task: Update handler unit tests in `internal/transit/http/transit_key_handler_test.go`. +- [x] Task: Conductor - User Manual Verification 'Phase 2: HTTP Handler and Routing Updates' (Protocol in workflow.md) + +## Phase 3: Integration Tests and Documentation +- [x] Task: Update integration tests in `test/integration/transit_flow_test.go` to use the transit key name for the DELETE request. +- [x] Task: Update `docs/openapi.yaml` to change `{id}` to `{name}` for the `DELETE /v1/transit/keys/{id}` endpoint and update its description. +- [x] Task: Update `docs/engines/transit.md` to ensure the Delete Transit Key section uses `:name`. +- [x] Task: Update `docs/concepts/api-fundamentals.md` and `docs/auth/policies.md` to change `:id` to `:name` for the transit delete endpoint. +- [x] Task: Conductor - User Manual Verification 'Phase 3: Integration Tests and Documentation' (Protocol in workflow.md) diff --git a/conductor/archive/fix_transit_key_delete_inconsistency_20260309/spec.md b/conductor/archive/fix_transit_key_delete_inconsistency_20260309/spec.md new file mode 100644 index 0000000..8ef9378 --- /dev/null +++ b/conductor/archive/fix_transit_key_delete_inconsistency_20260309/spec.md @@ -0,0 +1,28 @@ +# Specification: Fix Transit Key Delete Endpoint Inconsistency + +## Overview +This track addresses an inconsistency in the Transit engine API. Currently, the `DELETE /v1/transit/keys/:id` endpoint uses a UUID (`:id`), while all other transit endpoints (GET, POST rotate/encrypt/decrypt) use the human-readable `:name`. This fix will align the DELETE endpoint with the rest of the API and the Tokenization engine's behavior. + +## Functional Requirements +- Change the `DELETE /v1/transit/keys/:id` endpoint to `DELETE /v1/transit/keys/:name` in the router. +- The endpoint must accept a string `:name` instead of a UUID `:id`. +- Soft-delete all versions of the transit key associated with the given name. +- Update the `TransitKeyUseCase.Delete` method signature to accept `name string` instead of `transitKeyID uuid.UUID`. +- Update the `TransitKeyRepository.Delete` method signature to accept `name string` and update all matching records' `deleted_at` field. +- Update both PostgreSQL and MySQL repository implementations. +- Ensure proper authorization is maintained (requires `delete` capability on the path `/v1/transit/keys/:name`). +- Update the integration tests in `test/integration/transit_flow_test.go` to use `:name` instead of `:id` for deletion testing. + +## Non-Functional Requirements +- Maintain API consistency across the platform. +- Ensure no regressions in other transit operations. + +## Acceptance Criteria +- `DELETE /v1/transit/keys/:name` successfully soft-deletes all versions of the key. +- Integration tests confirm that all versions are marked as deleted. +- Unit tests for the handler, use case, and repository are updated/added. +- Documentation (`docs/openapi.yaml`, `docs/engines/transit.md`, `docs/concepts/api-fundamentals.md`, `docs/auth/policies.md`) reflects the change. + +## Out of Scope +- Changing other engines' endpoints (Tokenization is already consistent). +- Hard-deletion of keys (remains a separate process/track). diff --git a/docs/auth/policies.md b/docs/auth/policies.md index bc5e5c7..b89b2e7 100644 --- a/docs/auth/policies.md +++ b/docs/auth/policies.md @@ -107,7 +107,7 @@ Endpoint capability intent (quick map, condensed from [Capability matrix](../con | --- | --- | | `GET /v1/clients`, `GET /v1/audit-logs`, `POST /v1/tokenization/validate` | `read` | | `POST /v1/clients`, `PUT /v1/clients/:id`, `POST /v1/transit/keys`, `POST /v1/tokenization/keys` | `write` | -| `DELETE /v1/token`, `DELETE /v1/clients/:id/tokens`, `DELETE /v1/clients/:id`, `DELETE /v1/transit/keys/:id`, `DELETE /v1/tokenization/keys/:id`, `POST /v1/tokenization/revoke` | `delete` | +| `DELETE /v1/token`, `DELETE /v1/clients/:id/tokens`, `DELETE /v1/clients/:id`, `DELETE /v1/transit/keys/:name`, `DELETE /v1/tokenization/keys/:id`, `POST /v1/tokenization/revoke` | `delete` | | `POST /v1/secrets/*path`, `POST /v1/transit/keys/:name/encrypt`, `POST /v1/tokenization/keys/:name/tokenize` | `encrypt` | | `GET /v1/secrets/*path`, `POST /v1/transit/keys/:name/decrypt`, `POST /v1/tokenization/detokenize` | `decrypt` | | `POST /v1/transit/keys/:name/rotate`, `POST /v1/tokenization/keys/:name/rotate` | `rotate` | diff --git a/docs/concepts/api-fundamentals.md b/docs/concepts/api-fundamentals.md index cfdf850..e90cde6 100644 --- a/docs/concepts/api-fundamentals.md +++ b/docs/concepts/api-fundamentals.md @@ -63,7 +63,7 @@ First place to look: - `GET /v1/transit/keys` requires `read` - `POST /v1/transit/keys` requires `write` - `POST /v1/transit/keys/:name/rotate` requires `rotate` -- `DELETE /v1/transit/keys/:id` requires `delete` +- `DELETE /v1/transit/keys/:name` requires `delete` - `POST /v1/transit/keys/:name/encrypt` requires `encrypt` - `POST /v1/transit/keys/:name/decrypt` requires `decrypt` - `GET /v1/tokenization/keys` requires `read` @@ -109,7 +109,7 @@ This section is the canonical capability-to-endpoint reference used by API docs | `GET /v1/transit/keys` | `read` | | `POST /v1/transit/keys` | `write` | | `POST /v1/transit/keys/:name/rotate` | `rotate` | -| `DELETE /v1/transit/keys/:id` | `delete` | +| `DELETE /v1/transit/keys/:name` | `delete` | | `POST /v1/transit/keys/:name/encrypt` | `encrypt` | | `POST /v1/transit/keys/:name/decrypt` | `decrypt` | | `GET /v1/tokenization/keys` | `read` | diff --git a/docs/openapi.yaml b/docs/openapi.yaml index d7e4579..2753afa 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -520,31 +520,14 @@ paths: $ref: "#/components/schemas/ErrorResponse" "429": $ref: "#/components/responses/TooManyRequests" - /v1/transit/keys/{name}/rotate: - post: + delete: tags: [transit] - summary: Rotate transit key + summary: Delete transit key security: - bearerAuth: [] - parameters: - - name: name - in: path - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/TransitKeyRotateRequest" responses: - "201": - description: New transit key version created - content: - application/json: - schema: - $ref: "#/components/schemas/TransitKeyResponse" + "204": + description: Deleted "401": $ref: "#/components/responses/Unauthorized" "403": @@ -559,22 +542,31 @@ paths: $ref: "#/components/responses/ValidationError" "429": $ref: "#/components/responses/TooManyRequests" - /v1/transit/keys/{id}: - delete: + /v1/transit/keys/{name}/rotate: + post: tags: [transit] - summary: Delete transit key + summary: Rotate transit key security: - bearerAuth: [] parameters: - - name: id + - name: name in: path required: true schema: type: string - format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/TransitKeyRotateRequest" responses: - "204": - description: Deleted + "201": + description: New transit key version created + content: + application/json: + schema: + $ref: "#/components/schemas/TransitKeyResponse" "401": $ref: "#/components/responses/Unauthorized" "403": diff --git a/internal/http/server.go b/internal/http/server.go index beb440d..86b6e4b 100644 --- a/internal/http/server.go +++ b/internal/http/server.go @@ -335,7 +335,7 @@ func (s *Server) registerTransitRoutes( ) // Delete transit key - keys.DELETE("/:id", + keys.DELETE("/:name", authHTTP.AuthorizationMiddleware(authDomain.DeleteCapability, auditLogUseCase, s.logger), transitKeyHandler.DeleteHandler, ) diff --git a/internal/transit/domain/repository.go b/internal/transit/domain/repository.go index 11c119d..9d19f97 100644 --- a/internal/transit/domain/repository.go +++ b/internal/transit/domain/repository.go @@ -23,8 +23,8 @@ type TransitKeyRepository interface { // Create stores a new transit key in the repository using transaction support from context. Create(ctx context.Context, transitKey *TransitKey) error - // Delete soft deletes a transit key by marking it with DeletedAt timestamp. - Delete(ctx context.Context, transitKeyID uuid.UUID) error + // Delete soft deletes all versions of a transit key by name, marking them with DeletedAt timestamp. + Delete(ctx context.Context, name string) error // GetByName retrieves the latest version of a transit key by name. Returns ErrTransitKeyNotFound if not found. GetByName(ctx context.Context, name string) (*TransitKey, error) diff --git a/internal/transit/http/transit_key_handler.go b/internal/transit/http/transit_key_handler.go index 77bdc14..78117e5 100644 --- a/internal/transit/http/transit_key_handler.go +++ b/internal/transit/http/transit_key_handler.go @@ -8,7 +8,6 @@ import ( "strconv" "github.com/gin-gonic/gin" - "github.com/google/uuid" "github.com/allisson/secrets/internal/httputil" "github.com/allisson/secrets/internal/transit/http/dto" @@ -119,21 +118,23 @@ func (h *TransitKeyHandler) RotateHandler(c *gin.Context) { c.JSON(http.StatusCreated, response) } -// DeleteHandler soft deletes a transit key by ID. -// DELETE /v1/transit/keys/:id - Requires DeleteCapability on path /v1/transit/keys/:id. +// DeleteHandler soft deletes a transit key by name. +// DELETE /v1/transit/keys/:name - Requires DeleteCapability on path /v1/transit/keys/:name. // Returns 204 No Content. func (h *TransitKeyHandler) DeleteHandler(c *gin.Context) { - // Parse and validate UUID - transitKeyID, err := uuid.Parse(c.Param("id")) - if err != nil { - httputil.HandleBadRequestGin(c, - fmt.Errorf("invalid transit key ID format: must be a valid UUID"), - h.logger) + // Extract and validate name from URL parameter + name := c.Param("name") + if name == "" { + httputil.HandleBadRequestGin( + c, + fmt.Errorf("transit key name cannot be empty"), + h.logger, + ) return } // Call use case - if err := h.transitKeyUseCase.Delete(c.Request.Context(), transitKeyID); err != nil { + if err := h.transitKeyUseCase.Delete(c.Request.Context(), name); err != nil { httputil.HandleErrorGin(c, err, h.logger) return } diff --git a/internal/transit/http/transit_key_handler_test.go b/internal/transit/http/transit_key_handler_test.go index d71f430..2b0a057 100644 --- a/internal/transit/http/transit_key_handler_test.go +++ b/internal/transit/http/transit_key_handler_test.go @@ -311,18 +311,18 @@ func TestTransitKeyHandler_RotateHandler(t *testing.T) { } func TestTransitKeyHandler_DeleteHandler(t *testing.T) { - t.Run("Success_ValidUUID", func(t *testing.T) { + t.Run("Success_ValidName", func(t *testing.T) { handler, mockUseCase := setupTestTransitKeyHandler(t) - transitKeyID := uuid.Must(uuid.NewV7()) + name := "test-key" mockUseCase.EXPECT(). - Delete(mock.Anything, transitKeyID). + Delete(mock.Anything, name). Return(nil). Once() - c, w := createTestContext(http.MethodDelete, fmt.Sprintf("/v1/transit/keys/%s", transitKeyID), nil) - c.Params = gin.Params{gin.Param{Key: "id", Value: transitKeyID.String()}} + c, w := createTestContext(http.MethodDelete, fmt.Sprintf("/v1/transit/keys/%s", name), nil) + c.Params = gin.Params{gin.Param{Key: "name", Value: name}} handler.DeleteHandler(c) @@ -330,34 +330,18 @@ func TestTransitKeyHandler_DeleteHandler(t *testing.T) { assert.Empty(t, w.Body.String()) }) - t.Run("Error_InvalidUUID", func(t *testing.T) { - handler, _ := setupTestTransitKeyHandler(t) - - c, w := createTestContext(http.MethodDelete, "/v1/transit/keys/invalid-uuid", nil) - c.Params = gin.Params{gin.Param{Key: "id", Value: "invalid-uuid"}} - - handler.DeleteHandler(c) - - assert.Equal(t, http.StatusBadRequest, w.Code) - - var response map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &response) - assert.NoError(t, err) - assert.Equal(t, "bad_request", response["error"]) - }) - t.Run("Error_TransitKeyNotFound", func(t *testing.T) { handler, mockUseCase := setupTestTransitKeyHandler(t) - transitKeyID := uuid.Must(uuid.NewV7()) + name := "nonexistent-key" mockUseCase.EXPECT(). - Delete(mock.Anything, transitKeyID). + Delete(mock.Anything, name). Return(transitDomain.ErrTransitKeyNotFound). Once() - c, w := createTestContext(http.MethodDelete, fmt.Sprintf("/v1/transit/keys/%s", transitKeyID), nil) - c.Params = gin.Params{gin.Param{Key: "id", Value: transitKeyID.String()}} + c, w := createTestContext(http.MethodDelete, fmt.Sprintf("/v1/transit/keys/%s", name), nil) + c.Params = gin.Params{gin.Param{Key: "name", Value: name}} handler.DeleteHandler(c) diff --git a/internal/transit/repository/mysql/mysql_transit_key_repository.go b/internal/transit/repository/mysql/mysql_transit_key_repository.go index 237095e..431ecc8 100644 --- a/internal/transit/repository/mysql/mysql_transit_key_repository.go +++ b/internal/transit/repository/mysql/mysql_transit_key_repository.go @@ -6,8 +6,6 @@ import ( "errors" "time" - "github.com/google/uuid" - cryptoDomain "github.com/allisson/secrets/internal/crypto/domain" "github.com/allisson/secrets/internal/database" apperrors "github.com/allisson/secrets/internal/errors" @@ -52,18 +50,13 @@ func (m *MySQLTransitKeyRepository) Create(ctx context.Context, transitKey *tran return nil } -// Delete soft-deletes a transit key by setting its deleted_at timestamp. -func (m *MySQLTransitKeyRepository) Delete(ctx context.Context, transitKeyID uuid.UUID) error { +// Delete soft-deletes all versions of a transit key by name. +func (m *MySQLTransitKeyRepository) Delete(ctx context.Context, name string) error { querier := database.GetTx(ctx, m.db) - query := `UPDATE transit_keys SET deleted_at = NOW() WHERE id = ?` - - id, err := transitKeyID.MarshalBinary() - if err != nil { - return apperrors.Wrap(err, "failed to marshal transit key id") - } + query := `UPDATE transit_keys SET deleted_at = NOW() WHERE name = ? AND deleted_at IS NULL` - _, err = querier.ExecContext(ctx, query, id) + _, err := querier.ExecContext(ctx, query, name) if err != nil { return apperrors.Wrap(err, "failed to delete transit key") } diff --git a/internal/transit/repository/mysql/mysql_transit_key_repository_test.go b/internal/transit/repository/mysql/mysql_transit_key_repository_test.go index b942ce7..2ddb93b 100644 --- a/internal/transit/repository/mysql/mysql_transit_key_repository_test.go +++ b/internal/transit/repository/mysql/mysql_transit_key_repository_test.go @@ -143,30 +143,29 @@ func TestMySQLTransitKeyRepository_Delete(t *testing.T) { dekID := createTestDekMySQL(t, db) - // Create a transit key - transitKey := &transitDomain.TransitKey{ - ID: uuid.Must(uuid.NewV7()), - Name: "test-key", - Version: 1, - DekID: dekID, - CreatedAt: time.Now().UTC(), + // Create multiple versions of a transit key + name := "test-key" + for i := 1; i <= 3; i++ { + transitKey := &transitDomain.TransitKey{ + ID: uuid.Must(uuid.NewV7()), + Name: name, + Version: uint(i), + DekID: dekID, + CreatedAt: time.Now().UTC(), + } + require.NoError(t, repo.Create(ctx, transitKey)) } - err := repo.Create(ctx, transitKey) - require.NoError(t, err) - - // Delete the transit key (soft delete) - err = repo.Delete(ctx, transitKey.ID) - require.NoError(t, err) - // Verify the key still exists but has deleted_at set - var deletedAt *time.Time - transitKeyIDBytes, err := transitKey.ID.MarshalBinary() + // Delete the transit key by name (soft delete all versions) + err := repo.Delete(ctx, name) require.NoError(t, err) - query := `SELECT deleted_at FROM transit_keys WHERE id = ?` - err = db.QueryRowContext(ctx, query, transitKeyIDBytes).Scan(&deletedAt) + // Verify all versions have deleted_at set + var count int + query := `SELECT COUNT(*) FROM transit_keys WHERE name = ? AND deleted_at IS NOT NULL` + err = db.QueryRowContext(ctx, query, name).Scan(&count) require.NoError(t, err) - assert.NotNil(t, deletedAt, "deleted_at should be set after soft delete") + assert.Equal(t, 3, count, "all 3 versions should be soft-deleted") } func TestMySQLTransitKeyRepository_GetByName_Success(t *testing.T) { @@ -281,40 +280,42 @@ func TestMySQLTransitKeyRepository_GetByName_IgnoresDeletedKeys(t *testing.T) { dekID := createTestDekMySQL(t, db) - // Create version 1 - key1 := &transitDomain.TransitKey{ + // Create version 1 of key A + key1A := &transitDomain.TransitKey{ ID: uuid.Must(uuid.NewV7()), - Name: "deleted-key-test", + Name: "key-A", Version: 1, DekID: dekID, CreatedAt: time.Now().UTC(), } - err := repo.Create(ctx, key1) + err := repo.Create(ctx, key1A) require.NoError(t, err) - // Create version 2 - time.Sleep(time.Millisecond) - key2 := &transitDomain.TransitKey{ + // Create version 1 of key B + key1B := &transitDomain.TransitKey{ ID: uuid.Must(uuid.NewV7()), - Name: "deleted-key-test", - Version: 2, + Name: "key-B", + Version: 1, DekID: dekID, CreatedAt: time.Now().UTC(), } - err = repo.Create(ctx, key2) + err = repo.Create(ctx, key1B) require.NoError(t, err) - // Delete version 2 (the latest) - err = repo.Delete(ctx, key2.ID) + // Delete key B + err = repo.Delete(ctx, "key-B") require.NoError(t, err) - // GetByName should return version 1 (since version 2 is deleted) - retrievedKey, err := repo.GetByName(ctx, "deleted-key-test") - require.NoError(t, err) - require.NotNil(t, retrievedKey) + // GetByName for key B should return not found + retrievedKeyB, err := repo.GetByName(ctx, "key-B") + assert.ErrorIs(t, err, transitDomain.ErrTransitKeyNotFound) + assert.Nil(t, retrievedKeyB) - assert.Equal(t, uint(1), retrievedKey.Version) - assert.Equal(t, key1.ID, retrievedKey.ID) + // GetByName for key A should still return version 1 + retrievedKeyA, err := repo.GetByName(ctx, "key-A") + require.NoError(t, err) + require.NotNil(t, retrievedKeyA) + assert.Equal(t, "key-A", retrievedKeyA.Name) } func TestMySQLTransitKeyRepository_GetByName_AllVersionsDeleted(t *testing.T) { @@ -328,9 +329,10 @@ func TestMySQLTransitKeyRepository_GetByName_AllVersionsDeleted(t *testing.T) { dekID := createTestDekMySQL(t, db) // Create a transit key + name := "all-deleted-test" transitKey := &transitDomain.TransitKey{ ID: uuid.Must(uuid.NewV7()), - Name: "all-deleted-test", + Name: name, Version: 1, DekID: dekID, CreatedAt: time.Now().UTC(), @@ -338,12 +340,12 @@ func TestMySQLTransitKeyRepository_GetByName_AllVersionsDeleted(t *testing.T) { err := repo.Create(ctx, transitKey) require.NoError(t, err) - // Delete the key - err = repo.Delete(ctx, transitKey.ID) + // Delete the key by name + err = repo.Delete(ctx, name) require.NoError(t, err) // GetByName should return not found error - retrievedKey, err := repo.GetByName(ctx, "all-deleted-test") + retrievedKey, err := repo.GetByName(ctx, name) assert.Error(t, err) assert.Nil(t, retrievedKey) assert.ErrorIs(t, err, transitDomain.ErrTransitKeyNotFound) @@ -427,12 +429,8 @@ func TestMySQLTransitKeyRepository_Delete_WithTransaction(t *testing.T) { tx, err := db.BeginTx(ctx, nil) require.NoError(t, err) - // Marshal UUID - id, err := transitKey.ID.MarshalBinary() - require.NoError(t, err) - // Delete within transaction - _, err = tx.ExecContext(ctx, `UPDATE transit_keys SET deleted_at = NOW() WHERE id = ?`, id) + _, err = tx.ExecContext(ctx, `UPDATE transit_keys SET deleted_at = NOW() WHERE name = ?`, transitKey.Name) require.NoError(t, err) // Rollback transaction @@ -705,8 +703,8 @@ func TestMySQLTransitKeyRepository_GetByNameAndVersion_IgnoresDeletedKeys(t *tes err = repo.Create(ctx, key2) require.NoError(t, err) - // Delete version 2 - err = repo.Delete(ctx, key2.ID) + // Delete version 2 (and 1) + err = repo.Delete(ctx, "deleted-version-test") require.NoError(t, err) // GetByNameAndVersion should not find version 2 (it's deleted) @@ -715,12 +713,11 @@ func TestMySQLTransitKeyRepository_GetByNameAndVersion_IgnoresDeletedKeys(t *tes assert.Nil(t, retrievedKey) assert.ErrorIs(t, err, transitDomain.ErrTransitKeyNotFound) - // GetByNameAndVersion should still find version 1 (not deleted) + // GetByNameAndVersion should also not find version 1 (it's also deleted) retrievedKey, err = repo.GetByNameAndVersion(ctx, "deleted-version-test", 1) - require.NoError(t, err) - require.NotNil(t, retrievedKey) - assert.Equal(t, uint(1), retrievedKey.Version) - assert.Equal(t, key1.ID, retrievedKey.ID) + assert.Error(t, err) + assert.Nil(t, retrievedKey) + assert.ErrorIs(t, err, transitDomain.ErrTransitKeyNotFound) } func TestMySQLTransitKeyRepository_GetByNameAndVersion_WithTransaction(t *testing.T) { diff --git a/internal/transit/repository/postgresql/postgresql_transit_key_repository.go b/internal/transit/repository/postgresql/postgresql_transit_key_repository.go index fb35f5f..ca5012f 100644 --- a/internal/transit/repository/postgresql/postgresql_transit_key_repository.go +++ b/internal/transit/repository/postgresql/postgresql_transit_key_repository.go @@ -8,8 +8,6 @@ import ( "errors" "time" - "github.com/google/uuid" - cryptoDomain "github.com/allisson/secrets/internal/crypto/domain" "github.com/allisson/secrets/internal/database" apperrors "github.com/allisson/secrets/internal/errors" @@ -47,13 +45,13 @@ func (p *PostgreSQLTransitKeyRepository) Create( return nil } -// Delete soft-deletes a transit key by setting its deleted_at timestamp. -func (p *PostgreSQLTransitKeyRepository) Delete(ctx context.Context, transitKeyID uuid.UUID) error { +// Delete soft-deletes all versions of a transit key by name. +func (p *PostgreSQLTransitKeyRepository) Delete(ctx context.Context, name string) error { querier := database.GetTx(ctx, p.db) - query := `UPDATE transit_keys SET deleted_at = NOW() WHERE id = $1` + query := `UPDATE transit_keys SET deleted_at = NOW() WHERE name = $1 AND deleted_at IS NULL` - _, err := querier.ExecContext(ctx, query, transitKeyID) + _, err := querier.ExecContext(ctx, query, name) if err != nil { return apperrors.Wrap(err, "failed to delete transit key") } diff --git a/internal/transit/repository/postgresql/postgresql_transit_key_repository_test.go b/internal/transit/repository/postgresql/postgresql_transit_key_repository_test.go index 7a20233..fac602d 100644 --- a/internal/transit/repository/postgresql/postgresql_transit_key_repository_test.go +++ b/internal/transit/repository/postgresql/postgresql_transit_key_repository_test.go @@ -133,27 +133,29 @@ func TestPostgreSQLTransitKeyRepository_Delete(t *testing.T) { dekID := createTestDek(t, db) - // Create a transit key - transitKey := &transitDomain.TransitKey{ - ID: uuid.Must(uuid.NewV7()), - Name: "test-key", - Version: 1, - DekID: dekID, - CreatedAt: time.Now().UTC(), + // Create multiple versions of a transit key + name := "test-key" + for i := 1; i <= 3; i++ { + transitKey := &transitDomain.TransitKey{ + ID: uuid.Must(uuid.NewV7()), + Name: name, + Version: uint(i), + DekID: dekID, + CreatedAt: time.Now().UTC(), + } + require.NoError(t, repo.Create(ctx, transitKey)) } - err := repo.Create(ctx, transitKey) - require.NoError(t, err) - // Delete the transit key (soft delete) - err = repo.Delete(ctx, transitKey.ID) + // Delete the transit key by name (soft delete all versions) + err := repo.Delete(ctx, name) require.NoError(t, err) - // Verify the key still exists but has deleted_at set - var deletedAt *time.Time - query := `SELECT deleted_at FROM transit_keys WHERE id = $1` - err = db.QueryRowContext(ctx, query, transitKey.ID).Scan(&deletedAt) + // Verify all versions have deleted_at set + var count int + query := `SELECT COUNT(*) FROM transit_keys WHERE name = $1 AND deleted_at IS NOT NULL` + err = db.QueryRowContext(ctx, query, name).Scan(&count) require.NoError(t, err) - assert.NotNil(t, deletedAt, "deleted_at should be set after soft delete") + assert.Equal(t, 3, count, "all 3 versions should be soft-deleted") } func TestPostgreSQLTransitKeyRepository_GetByName_Success(t *testing.T) { @@ -268,40 +270,42 @@ func TestPostgreSQLTransitKeyRepository_GetByName_IgnoresDeletedKeys(t *testing. dekID := createTestDek(t, db) - // Create version 1 - key1 := &transitDomain.TransitKey{ + // Create version 1 of key A + key1A := &transitDomain.TransitKey{ ID: uuid.Must(uuid.NewV7()), - Name: "deleted-key-test", + Name: "key-A", Version: 1, DekID: dekID, CreatedAt: time.Now().UTC(), } - err := repo.Create(ctx, key1) + err := repo.Create(ctx, key1A) require.NoError(t, err) - // Create version 2 - time.Sleep(time.Millisecond) - key2 := &transitDomain.TransitKey{ + // Create version 1 of key B + key1B := &transitDomain.TransitKey{ ID: uuid.Must(uuid.NewV7()), - Name: "deleted-key-test", - Version: 2, + Name: "key-B", + Version: 1, DekID: dekID, CreatedAt: time.Now().UTC(), } - err = repo.Create(ctx, key2) + err = repo.Create(ctx, key1B) require.NoError(t, err) - // Delete version 2 (the latest) - err = repo.Delete(ctx, key2.ID) + // Delete key B + err = repo.Delete(ctx, "key-B") require.NoError(t, err) - // GetByName should return version 1 (since version 2 is deleted) - retrievedKey, err := repo.GetByName(ctx, "deleted-key-test") - require.NoError(t, err) - require.NotNil(t, retrievedKey) + // GetByName for key B should return not found + retrievedKeyB, err := repo.GetByName(ctx, "key-B") + assert.ErrorIs(t, err, transitDomain.ErrTransitKeyNotFound) + assert.Nil(t, retrievedKeyB) - assert.Equal(t, uint(1), retrievedKey.Version) - assert.Equal(t, key1.ID, retrievedKey.ID) + // GetByName for key A should still return version 1 + retrievedKeyA, err := repo.GetByName(ctx, "key-A") + require.NoError(t, err) + require.NotNil(t, retrievedKeyA) + assert.Equal(t, "key-A", retrievedKeyA.Name) } func TestPostgreSQLTransitKeyRepository_GetByName_AllVersionsDeleted(t *testing.T) { @@ -315,9 +319,10 @@ func TestPostgreSQLTransitKeyRepository_GetByName_AllVersionsDeleted(t *testing. dekID := createTestDek(t, db) // Create a transit key + name := "all-deleted-test" transitKey := &transitDomain.TransitKey{ ID: uuid.Must(uuid.NewV7()), - Name: "all-deleted-test", + Name: name, Version: 1, DekID: dekID, CreatedAt: time.Now().UTC(), @@ -325,12 +330,12 @@ func TestPostgreSQLTransitKeyRepository_GetByName_AllVersionsDeleted(t *testing. err := repo.Create(ctx, transitKey) require.NoError(t, err) - // Delete the key - err = repo.Delete(ctx, transitKey.ID) + // Delete the key by name + err = repo.Delete(ctx, name) require.NoError(t, err) // GetByName should return not found error - retrievedKey, err := repo.GetByName(ctx, "all-deleted-test") + retrievedKey, err := repo.GetByName(ctx, name) assert.Error(t, err) assert.Nil(t, retrievedKey) assert.ErrorIs(t, err, transitDomain.ErrTransitKeyNotFound) @@ -409,7 +414,7 @@ func TestPostgreSQLTransitKeyRepository_Delete_WithTransaction(t *testing.T) { require.NoError(t, err) // Delete within transaction - _, err = tx.ExecContext(ctx, `UPDATE transit_keys SET deleted_at = NOW() WHERE id = $1`, transitKey.ID) + _, err = tx.ExecContext(ctx, `UPDATE transit_keys SET deleted_at = NOW() WHERE name = $1`, transitKey.Name) require.NoError(t, err) // Rollback transaction @@ -668,8 +673,8 @@ func TestPostgreSQLTransitKeyRepository_GetByNameAndVersion_IgnoresDeletedKeys(t err = repo.Create(ctx, key2) require.NoError(t, err) - // Delete version 2 - err = repo.Delete(ctx, key2.ID) + // Delete version 2 (and 1) + err = repo.Delete(ctx, "deleted-version-test") require.NoError(t, err) // GetByNameAndVersion should not find version 2 (it's deleted) @@ -678,12 +683,11 @@ func TestPostgreSQLTransitKeyRepository_GetByNameAndVersion_IgnoresDeletedKeys(t assert.Nil(t, retrievedKey) assert.ErrorIs(t, err, transitDomain.ErrTransitKeyNotFound) - // GetByNameAndVersion should still find version 1 (not deleted) + // GetByNameAndVersion should also not find version 1 (it's also deleted) retrievedKey, err = repo.GetByNameAndVersion(ctx, "deleted-version-test", 1) - require.NoError(t, err) - require.NotNil(t, retrievedKey) - assert.Equal(t, uint(1), retrievedKey.Version) - assert.Equal(t, key1.ID, retrievedKey.ID) + assert.Error(t, err) + assert.Nil(t, retrievedKey) + assert.ErrorIs(t, err, transitDomain.ErrTransitKeyNotFound) } func TestPostgreSQLTransitKeyRepository_GetByNameAndVersion_WithTransaction(t *testing.T) { diff --git a/internal/transit/usecase/interface.go b/internal/transit/usecase/interface.go index 947d14a..d4f8225 100644 --- a/internal/transit/usecase/interface.go +++ b/internal/transit/usecase/interface.go @@ -5,8 +5,6 @@ package usecase import ( "context" - "github.com/google/uuid" - cryptoDomain "github.com/allisson/secrets/internal/crypto/domain" transitDomain "github.com/allisson/secrets/internal/transit/domain" ) @@ -34,8 +32,8 @@ type TransitKeyUseCase interface { version uint, ) (*transitDomain.TransitKey, cryptoDomain.Algorithm, error) - // Delete soft deletes a transit key and all its versions by transit key ID. - Delete(ctx context.Context, transitKeyID uuid.UUID) error + // Delete soft deletes a transit key and all its versions by name. + Delete(ctx context.Context, name string) error // Encrypt encrypts plaintext using the latest version of the named transit key. // Optional context (AAD) can be provided for additional security. diff --git a/internal/transit/usecase/metrics_decorator.go b/internal/transit/usecase/metrics_decorator.go index 242b007..07b43f8 100644 --- a/internal/transit/usecase/metrics_decorator.go +++ b/internal/transit/usecase/metrics_decorator.go @@ -4,8 +4,6 @@ import ( "context" "time" - "github.com/google/uuid" - cryptoDomain "github.com/allisson/secrets/internal/crypto/domain" "github.com/allisson/secrets/internal/metrics" transitDomain "github.com/allisson/secrets/internal/transit/domain" @@ -86,9 +84,9 @@ func (t *transitKeyUseCaseWithMetrics) Get( } // Delete records metrics for transit key deletion operations. -func (t *transitKeyUseCaseWithMetrics) Delete(ctx context.Context, transitKeyID uuid.UUID) error { +func (t *transitKeyUseCaseWithMetrics) Delete(ctx context.Context, name string) error { start := time.Now() - err := t.next.Delete(ctx, transitKeyID) + err := t.next.Delete(ctx, name) status := "success" if err != nil { diff --git a/internal/transit/usecase/metrics_decorator_test.go b/internal/transit/usecase/metrics_decorator_test.go index 03f9817..8107330 100644 --- a/internal/transit/usecase/metrics_decorator_test.go +++ b/internal/transit/usecase/metrics_decorator_test.go @@ -154,18 +154,18 @@ func TestTransitKeyUseCaseWithMetrics_Delete(t *testing.T) { uc := usecase.NewTransitKeyUseCaseWithMetrics(mockNext, mockMetrics) ctx := context.Background() - transitKeyID := uuid.Must(uuid.NewV7()) + name := "test-key" t.Run("Delete_Success", func(t *testing.T) { // Arrange - mockNext.EXPECT().Delete(ctx, transitKeyID).Return(nil).Once() + mockNext.EXPECT().Delete(ctx, name).Return(nil).Once() mockMetrics.On("RecordOperation", ctx, "transit", "transit_key_delete", "success").Return().Once() mockMetrics.On("RecordDuration", ctx, "transit", "transit_key_delete", mock.AnythingOfType("time.Duration"), "success"). Return(). Once() // Act - err := uc.Delete(ctx, transitKeyID) + err := uc.Delete(ctx, name) // Assert assert.NoError(t, err) @@ -177,14 +177,14 @@ func TestTransitKeyUseCaseWithMetrics_Delete(t *testing.T) { // Arrange expectedErr := errors.New("deletion failed") - mockNext.EXPECT().Delete(ctx, transitKeyID).Return(expectedErr).Once() + mockNext.EXPECT().Delete(ctx, name).Return(expectedErr).Once() mockMetrics.On("RecordOperation", ctx, "transit", "transit_key_delete", "error").Return().Once() mockMetrics.On("RecordDuration", ctx, "transit", "transit_key_delete", mock.AnythingOfType("time.Duration"), "error"). Return(). Once() // Act - err := uc.Delete(ctx, transitKeyID) + err := uc.Delete(ctx, name) // Assert assert.Error(t, err) diff --git a/internal/transit/usecase/mocks/mocks.go b/internal/transit/usecase/mocks/mocks.go index e0ec428..35f49f9 100644 --- a/internal/transit/usecase/mocks/mocks.go +++ b/internal/transit/usecase/mocks/mocks.go @@ -251,16 +251,16 @@ func (_c *MockTransitKeyRepository_Create_Call) RunAndReturn(run func(ctx contex } // Delete provides a mock function for the type MockTransitKeyRepository -func (_mock *MockTransitKeyRepository) Delete(ctx context.Context, transitKeyID uuid.UUID) error { - ret := _mock.Called(ctx, transitKeyID) +func (_mock *MockTransitKeyRepository) Delete(ctx context.Context, name string) error { + ret := _mock.Called(ctx, name) if len(ret) == 0 { panic("no return value specified for Delete") } var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, uuid.UUID) error); ok { - r0 = returnFunc(ctx, transitKeyID) + if returnFunc, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = returnFunc(ctx, name) } else { r0 = ret.Error(0) } @@ -274,20 +274,20 @@ type MockTransitKeyRepository_Delete_Call struct { // Delete is a helper method to define mock.On call // - ctx context.Context -// - transitKeyID uuid.UUID -func (_e *MockTransitKeyRepository_Expecter) Delete(ctx interface{}, transitKeyID interface{}) *MockTransitKeyRepository_Delete_Call { - return &MockTransitKeyRepository_Delete_Call{Call: _e.mock.On("Delete", ctx, transitKeyID)} +// - name string +func (_e *MockTransitKeyRepository_Expecter) Delete(ctx interface{}, name interface{}) *MockTransitKeyRepository_Delete_Call { + return &MockTransitKeyRepository_Delete_Call{Call: _e.mock.On("Delete", ctx, name)} } -func (_c *MockTransitKeyRepository_Delete_Call) Run(run func(ctx context.Context, transitKeyID uuid.UUID)) *MockTransitKeyRepository_Delete_Call { +func (_c *MockTransitKeyRepository_Delete_Call) Run(run func(ctx context.Context, name string)) *MockTransitKeyRepository_Delete_Call { _c.Call.Run(func(args mock.Arguments) { var arg0 context.Context if args[0] != nil { arg0 = args[0].(context.Context) } - var arg1 uuid.UUID + var arg1 string if args[1] != nil { - arg1 = args[1].(uuid.UUID) + arg1 = args[1].(string) } run( arg0, @@ -302,7 +302,7 @@ func (_c *MockTransitKeyRepository_Delete_Call) Return(err error) *MockTransitKe return _c } -func (_c *MockTransitKeyRepository_Delete_Call) RunAndReturn(run func(ctx context.Context, transitKeyID uuid.UUID) error) *MockTransitKeyRepository_Delete_Call { +func (_c *MockTransitKeyRepository_Delete_Call) RunAndReturn(run func(ctx context.Context, name string) error) *MockTransitKeyRepository_Delete_Call { _c.Call.Return(run) return _c } @@ -857,16 +857,16 @@ func (_c *MockTransitKeyUseCase_Decrypt_Call) RunAndReturn(run func(ctx context. } // Delete provides a mock function for the type MockTransitKeyUseCase -func (_mock *MockTransitKeyUseCase) Delete(ctx context.Context, transitKeyID uuid.UUID) error { - ret := _mock.Called(ctx, transitKeyID) +func (_mock *MockTransitKeyUseCase) Delete(ctx context.Context, name string) error { + ret := _mock.Called(ctx, name) if len(ret) == 0 { panic("no return value specified for Delete") } var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, uuid.UUID) error); ok { - r0 = returnFunc(ctx, transitKeyID) + if returnFunc, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = returnFunc(ctx, name) } else { r0 = ret.Error(0) } @@ -880,20 +880,20 @@ type MockTransitKeyUseCase_Delete_Call struct { // Delete is a helper method to define mock.On call // - ctx context.Context -// - transitKeyID uuid.UUID -func (_e *MockTransitKeyUseCase_Expecter) Delete(ctx interface{}, transitKeyID interface{}) *MockTransitKeyUseCase_Delete_Call { - return &MockTransitKeyUseCase_Delete_Call{Call: _e.mock.On("Delete", ctx, transitKeyID)} +// - name string +func (_e *MockTransitKeyUseCase_Expecter) Delete(ctx interface{}, name interface{}) *MockTransitKeyUseCase_Delete_Call { + return &MockTransitKeyUseCase_Delete_Call{Call: _e.mock.On("Delete", ctx, name)} } -func (_c *MockTransitKeyUseCase_Delete_Call) Run(run func(ctx context.Context, transitKeyID uuid.UUID)) *MockTransitKeyUseCase_Delete_Call { +func (_c *MockTransitKeyUseCase_Delete_Call) Run(run func(ctx context.Context, name string)) *MockTransitKeyUseCase_Delete_Call { _c.Call.Run(func(args mock.Arguments) { var arg0 context.Context if args[0] != nil { arg0 = args[0].(context.Context) } - var arg1 uuid.UUID + var arg1 string if args[1] != nil { - arg1 = args[1].(uuid.UUID) + arg1 = args[1].(string) } run( arg0, @@ -908,7 +908,7 @@ func (_c *MockTransitKeyUseCase_Delete_Call) Return(err error) *MockTransitKeyUs return _c } -func (_c *MockTransitKeyUseCase_Delete_Call) RunAndReturn(run func(ctx context.Context, transitKeyID uuid.UUID) error) *MockTransitKeyUseCase_Delete_Call { +func (_c *MockTransitKeyUseCase_Delete_Call) RunAndReturn(run func(ctx context.Context, name string) error) *MockTransitKeyUseCase_Delete_Call { _c.Call.Return(run) return _c } diff --git a/internal/transit/usecase/transit_key_usecase.go b/internal/transit/usecase/transit_key_usecase.go index 87259ef..87f6daf 100644 --- a/internal/transit/usecase/transit_key_usecase.go +++ b/internal/transit/usecase/transit_key_usecase.go @@ -160,9 +160,9 @@ func (t *transitKeyUseCase) Get( return t.transitRepo.GetTransitKey(ctx, name, version) } -// Delete soft-deletes a transit key by setting its deleted_at timestamp. -func (t *transitKeyUseCase) Delete(ctx context.Context, transitKeyID uuid.UUID) error { - return t.transitRepo.Delete(ctx, transitKeyID) +// Delete soft-deletes all versions of a transit key by name. +func (t *transitKeyUseCase) Delete(ctx context.Context, name string) error { + return t.transitRepo.Delete(ctx, name) } // Encrypt encrypts plaintext using the latest version of a named transit key. diff --git a/internal/transit/usecase/transit_key_usecase_test.go b/internal/transit/usecase/transit_key_usecase_test.go index 18f72f3..a57c271 100644 --- a/internal/transit/usecase/transit_key_usecase_test.go +++ b/internal/transit/usecase/transit_key_usecase_test.go @@ -671,11 +671,11 @@ func TestTransitKeyUseCase_Delete(t *testing.T) { kekChain := createTestKekChain(kek.ID, kek) defer kekChain.Close() - transitKeyID := uuid.Must(uuid.NewV7()) + name := "test-key" // Setup expectations mockTransitRepo.EXPECT(). - Delete(ctx, transitKeyID). + Delete(ctx, name). Return(nil). Once() @@ -683,7 +683,7 @@ func TestTransitKeyUseCase_Delete(t *testing.T) { uc := NewTransitKeyUseCase( mockTxManager, mockTransitRepo, mockDekRepo, mockKeyManager, mockAeadManager, kekChain, ) - err := uc.Delete(ctx, transitKeyID) + err := uc.Delete(ctx, name) // Assert assert.NoError(t, err) @@ -702,12 +702,12 @@ func TestTransitKeyUseCase_Delete(t *testing.T) { kekChain := createTestKekChain(kek.ID, kek) defer kekChain.Close() - transitKeyID := uuid.Must(uuid.NewV7()) + name := "test-key" expectedError := errors.New("database error") // Setup expectations mockTransitRepo.EXPECT(). - Delete(ctx, transitKeyID). + Delete(ctx, name). Return(expectedError). Once() @@ -715,7 +715,7 @@ func TestTransitKeyUseCase_Delete(t *testing.T) { uc := NewTransitKeyUseCase( mockTxManager, mockTransitRepo, mockDekRepo, mockKeyManager, mockAeadManager, kekChain, ) - err := uc.Delete(ctx, transitKeyID) + err := uc.Delete(ctx, name) // Assert assert.Error(t, err) diff --git a/test/integration/additional_scenarios_test.go b/test/integration/additional_scenarios_test.go index 5b90c64..c936adb 100644 --- a/test/integration/additional_scenarios_test.go +++ b/test/integration/additional_scenarios_test.go @@ -12,7 +12,6 @@ import ( "testing" "time" - "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -313,7 +312,6 @@ func TestIntegration_Transit_DeleteKey(t *testing.T) { ctx := setupIntegrationTest(t, tc.dbDriver) defer teardownIntegrationTest(t, ctx) - var transitKeyID uuid.UUID keyName := "test-delete-key" plaintext := []byte("test-data") plaintextB64 := base64.StdEncoding.EncodeToString(plaintext) @@ -332,11 +330,6 @@ func TestIntegration_Transit_DeleteKey(t *testing.T) { err := json.Unmarshal(body, &response) require.NoError(t, err) assert.Equal(t, keyName, response.Name) - - // Store key ID for deletion (API requires ID, not name) - parsedID, err := uuid.Parse(response.ID) - require.NoError(t, err) - transitKeyID = parsedID }) // [2] Encrypt some data successfully @@ -349,9 +342,9 @@ func TestIntegration_Transit_DeleteKey(t *testing.T) { assert.Equal(t, http.StatusOK, resp.StatusCode, "encrypt should work before deletion") }) - // [3] Delete the key by ID (not name - API requires UUID) + // [3] Delete the key by name t.Run("03_DeleteKey", func(t *testing.T) { - resp, _ := ctx.makeRequest(t, http.MethodDelete, "/v1/transit/keys/"+transitKeyID.String(), nil, true) + resp, _ := ctx.makeRequest(t, http.MethodDelete, "/v1/transit/keys/"+keyName, nil, true) assert.Equal(t, http.StatusNoContent, resp.StatusCode) }) diff --git a/test/integration/transit_flow_test.go b/test/integration/transit_flow_test.go index acc997b..3585a1a 100644 --- a/test/integration/transit_flow_test.go +++ b/test/integration/transit_flow_test.go @@ -8,7 +8,6 @@ import ( "net/http" "testing" - "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -42,7 +41,6 @@ func TestIntegration_Transit_CompleteFlow(t *testing.T) { // Variables to store created resource IDs and encrypted data for later operations var ( transitKeyName = "integration-test-transit-key" - transitKeyID uuid.UUID plaintext1 = []byte("transit-test-data-1") plaintext2 = []byte("transit-test-data-2") ciphertext1 string // Encrypted with version 1 @@ -68,11 +66,6 @@ func TestIntegration_Transit_CompleteFlow(t *testing.T) { assert.Equal(t, uint(1), response.Version) assert.NotEmpty(t, response.DekID) assert.False(t, response.CreatedAt.IsZero()) - - // Store transit key ID for later deletion - parsedID, err := uuid.Parse(response.ID) - require.NoError(t, err) - transitKeyID = parsedID }) // [2/11] Test GET /v1/transit/keys/:name - Get transit key @@ -322,12 +315,12 @@ func TestIntegration_Transit_CompleteFlow(t *testing.T) { assert.Equal(t, http.StatusUnprocessableEntity, resp.StatusCode) }) - // [11/11] Test DELETE /v1/transit/keys/:id - Delete transit key + // [11/11] Test DELETE /v1/transit/keys/:name - Delete transit key t.Run("11_DeleteTransitKey", func(t *testing.T) { resp, body := ctx.makeRequest( t, http.MethodDelete, - "/v1/transit/keys/"+transitKeyID.String(), + "/v1/transit/keys/"+transitKeyName, nil, true, )