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, )