From b921754108b444d8df5bd478e742c04e865a6c2d Mon Sep 17 00:00:00 2001 From: Nayeon Kim Date: Sun, 2 Mar 2025 13:59:57 +0900 Subject: [PATCH 1/5] feat: Implement feature flag status update --- .../application/service/FeatureFlagService.kt | 11 +++++++ .../controller/FeatureFlagController.kt | 16 +++++---- .../presentation/model/StatusResponse.kt | 8 ++++- .../service/FeatureFlagServiceTest.kt | 33 +++++++++++++++++++ 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/backend/src/main/kotlin/com/lightswitch/application/service/FeatureFlagService.kt b/backend/src/main/kotlin/com/lightswitch/application/service/FeatureFlagService.kt index 6ab48bc..a1947a5 100644 --- a/backend/src/main/kotlin/com/lightswitch/application/service/FeatureFlagService.kt +++ b/backend/src/main/kotlin/com/lightswitch/application/service/FeatureFlagService.kt @@ -83,4 +83,15 @@ class FeatureFlagService( return featureFlagRepository.save(flag) } + + @Transactional + fun update( + user: User, + key: String, + enabled: Boolean, + ) { + val flag = getFlagOrThrow(key) + flag.enabled = enabled + featureFlagRepository.save(flag) + } } diff --git a/backend/src/main/kotlin/com/lightswitch/presentation/controller/FeatureFlagController.kt b/backend/src/main/kotlin/com/lightswitch/presentation/controller/FeatureFlagController.kt index 5e64878..90439ae 100644 --- a/backend/src/main/kotlin/com/lightswitch/presentation/controller/FeatureFlagController.kt +++ b/backend/src/main/kotlin/com/lightswitch/presentation/controller/FeatureFlagController.kt @@ -104,13 +104,15 @@ class FeatureFlagController( @PatchMapping("/{key}") fun updateFlagStatus( @PathVariable key: String, - @RequestParam status: String, - ): PayloadResponse { - return PayloadResponse( - status = "status", - message = "message", - data = null, - ) + @RequestParam status: Boolean, + ): StatusResponse { + // TODO: Improve the way finding the authenticated user. + val authentication = SecurityContextHolder.getContext().authentication + val userId = authentication.name + val user = userRepository.findByIdOrNull(userId.toLong()) ?: throw BusinessException("User not found") + + featureFlagService.update(user, key, status) + return StatusResponse.success("Successfully updated the status") } @Operation( diff --git a/backend/src/main/kotlin/com/lightswitch/presentation/model/StatusResponse.kt b/backend/src/main/kotlin/com/lightswitch/presentation/model/StatusResponse.kt index bc730eb..3cfab4d 100644 --- a/backend/src/main/kotlin/com/lightswitch/presentation/model/StatusResponse.kt +++ b/backend/src/main/kotlin/com/lightswitch/presentation/model/StatusResponse.kt @@ -3,4 +3,10 @@ package com.lightswitch.presentation.model data class StatusResponse( val status: String, val message: String, -) +) { + companion object { + fun success(message: String): StatusResponse { + return StatusResponse("Success", message) + } + } +} diff --git a/backend/src/test/kotlin/com/lightswitch/application/service/FeatureFlagServiceTest.kt b/backend/src/test/kotlin/com/lightswitch/application/service/FeatureFlagServiceTest.kt index aff250d..b65563f 100644 --- a/backend/src/test/kotlin/com/lightswitch/application/service/FeatureFlagServiceTest.kt +++ b/backend/src/test/kotlin/com/lightswitch/application/service/FeatureFlagServiceTest.kt @@ -454,6 +454,39 @@ class FeatureFlagServiceTest : BaseRepositoryTest() { .hasMessageContaining("Unsupported type: json") } + @Test + fun `update should update feature flag status`() { + val user = saveTestUser() + val flag = + featureFlagRepository.save( + FeatureFlag( + name = "test-flag", + description = "description", + type = Type.BOOLEAN, + enabled = false, + createdBy = user, + updatedBy = user, + ), + ) + + featureFlagService.update(user, "test-flag", true) + + val updatedFlag = featureFlagRepository.findById(flag.id!!.toInt()).orElseThrow() + + assertThat(updatedFlag.enabled).isTrue() + } + + @Test + fun `update should throw EntityNotFoundException when key does not exist`() { + val user = saveTestUser() + + assertThatThrownBy { + featureFlagService.update(user, "flag-one", true) + } + .isInstanceOf(EntityNotFoundException::class.java) + .hasMessageContaining("Feature flag flag-one does not exist") + } + private fun saveTestUser() = userRepository.save( User( From caa88f9bdda70675d6e3498bd8720890b7bffb10 Mon Sep 17 00:00:00 2001 From: Nayeon Kim Date: Sun, 2 Mar 2025 14:46:41 +0900 Subject: [PATCH 2/5] fix: Add missing @Valid annotation to PUT /flags/{key} --- .../presentation/controller/FeatureFlagController.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/com/lightswitch/presentation/controller/FeatureFlagController.kt b/backend/src/main/kotlin/com/lightswitch/presentation/controller/FeatureFlagController.kt index 90439ae..83ae188 100644 --- a/backend/src/main/kotlin/com/lightswitch/presentation/controller/FeatureFlagController.kt +++ b/backend/src/main/kotlin/com/lightswitch/presentation/controller/FeatureFlagController.kt @@ -83,7 +83,7 @@ class FeatureFlagController( @PutMapping("/{key}") fun updateFlag( @PathVariable key: String, - @RequestBody request: UpdateFeatureFlagRequest, + @RequestBody @Valid request: UpdateFeatureFlagRequest, ): PayloadResponse { // TODO: Improve the way finding the authenticated user. val authentication = SecurityContextHolder.getContext().authentication From 44e067b7dc9926af33d77825e331f77ca7f124d5 Mon Sep 17 00:00:00 2001 From: Nayeon Kim Date: Sun, 2 Mar 2025 14:47:01 +0900 Subject: [PATCH 3/5] fix: Format error message of MethodArgumentNotValidException --- .../presentation/controlleradvice/GlobalExceptionHandler.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/com/lightswitch/presentation/controlleradvice/GlobalExceptionHandler.kt b/backend/src/main/kotlin/com/lightswitch/presentation/controlleradvice/GlobalExceptionHandler.kt index c5c3b53..df19bfa 100644 --- a/backend/src/main/kotlin/com/lightswitch/presentation/controlleradvice/GlobalExceptionHandler.kt +++ b/backend/src/main/kotlin/com/lightswitch/presentation/controlleradvice/GlobalExceptionHandler.kt @@ -33,12 +33,13 @@ class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException::class) fun handleMethodArgumentNotValidException(ex: MethodArgumentNotValidException): ResponseEntity { log.error(ex.message, ex) + val errorMessages = ex.bindingResult.fieldErrors.joinToString(", ") { it.defaultMessage.toString() } return ResponseEntity .status(HttpStatus.BAD_REQUEST) .body( StatusResponse( status = HttpStatus.BAD_REQUEST.name, - message = ex.message, + message = errorMessages, ), ) } From e639a9acccd27d7df74c486758a3577ff2ba7af7 Mon Sep 17 00:00:00 2001 From: Nayeon Kim Date: Sun, 2 Mar 2025 14:47:22 +0900 Subject: [PATCH 4/5] test: Add presentation test for feature flag --- .../controller/FeatureFlagControllerTest.kt | 333 ++++++++++++++++-- 1 file changed, 312 insertions(+), 21 deletions(-) diff --git a/backend/src/test/kotlin/com/lightswitch/presentation/controller/FeatureFlagControllerTest.kt b/backend/src/test/kotlin/com/lightswitch/presentation/controller/FeatureFlagControllerTest.kt index b641eaf..535c359 100644 --- a/backend/src/test/kotlin/com/lightswitch/presentation/controller/FeatureFlagControllerTest.kt +++ b/backend/src/test/kotlin/com/lightswitch/presentation/controller/FeatureFlagControllerTest.kt @@ -9,7 +9,11 @@ import com.lightswitch.infrastructure.database.model.Type import com.lightswitch.infrastructure.database.repository.UserRepository import com.lightswitch.infrastructure.security.JwtTokenProvider import com.lightswitch.presentation.model.flag.CreateFeatureFlagRequest +import com.lightswitch.presentation.model.flag.UpdateFeatureFlagRequest +import jakarta.persistence.EntityNotFoundException +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.Mockito.doNothing import org.mockito.Mockito.`when` import org.mockito.junit.jupiter.MockitoExtension import org.springframework.beans.factory.annotation.Autowired @@ -19,7 +23,10 @@ import org.springframework.security.test.context.support.WithMockUser import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf import org.springframework.test.context.bean.override.mockito.MockitoBean import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status @@ -44,28 +51,36 @@ class FeatureFlagControllerTest { @MockitoBean private lateinit var jwtTokenProvider: JwtTokenProvider + private val user = User(id = 1L, username = "username", passwordHash = "passwordHash") + private val flag = + FeatureFlag( + id = 1L, + name = "test-flag", + description = "Test Flag", + type = Type.BOOLEAN, + enabled = true, + createdBy = user, + updatedBy = user, + ) + + @BeforeEach + fun setUp() { + flag.addDefaultCondition("boolean", false) + flag.addCondition("US", true) + flag.addCondition("KR", true) + } + @Test @WithMockUser(username = "1") - fun `should create feature flag successfully`() { - val user = User(id = 1L, username = "username", passwordHash = "passwordHash") + fun `POST flags should create feature flag successfully`() { val request = CreateFeatureFlagRequest( - key = "new-feature", + key = "test-flag", status = true, type = "BOOLEAN", defaultValue = mapOf("default" to true), description = "Test feature flag", ) - val flag = - FeatureFlag( - id = 1L, - name = "new-feature", - description = "Test feature flag", - type = Type.BOOLEAN, - enabled = true, - createdBy = user, - updatedBy = user, - ) val condition = Condition(id = 1L, key = "default", value = true, flag = flag) flag.defaultCondition = condition @@ -81,11 +96,11 @@ class FeatureFlagControllerTest { .andExpect(status().isOk) .andExpect(jsonPath("$.status").value("Success")) .andExpect(jsonPath("$.message").value("Created flag successfully")) - .andExpect(jsonPath("$.data.key").value("new-feature")) + .andExpect(jsonPath("$.data.key").value("test-flag")) .andExpect(jsonPath("$.data.status").value(true)) .andExpect(jsonPath("$.data.type").value("BOOLEAN")) .andExpect(jsonPath("$.data.defaultValue.default").value(true)) - .andExpect(jsonPath("$.data.description").value("Test feature flag")) + .andExpect(jsonPath("$.data.description").value("Test Flag")) .andExpect(jsonPath("$.data.createdBy").value("username")) .andExpect(jsonPath("$.data.updatedBy").value("username")) .andExpect(jsonPath("$.data.createdAt").exists()) @@ -95,7 +110,7 @@ class FeatureFlagControllerTest { @Test @WithMockUser(username = "1") - fun `should return 400 when request validation fails due to empty key`() { + fun `POST flags should return 400 when request validation fails due to empty key`() { val request = CreateFeatureFlagRequest( key = "", @@ -116,7 +131,7 @@ class FeatureFlagControllerTest { @Test @WithMockUser(username = "1") - fun `should return 400 when request validation fails due to empty type`() { + fun `POST flags should return 400 when request validation fails due to empty type`() { val request = CreateFeatureFlagRequest( key = "new-feature", @@ -137,7 +152,7 @@ class FeatureFlagControllerTest { @Test @WithMockUser(username = "1") - fun `should return 400 when request validation fails due to empty defaultValue`() { + fun `POST flags should return 400 when request validation fails due to empty defaultValue`() { val request = CreateFeatureFlagRequest( key = "new-feature", @@ -154,11 +169,13 @@ class FeatureFlagControllerTest { .content(objectMapper.writeValueAsString(request)), ) .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value("BAD_REQUEST")) + .andExpect(jsonPath("$.message").value("Default value is required.")) } @Test @WithMockUser(username = "1") - fun `should return 400 when request validation fails due to empty description`() { + fun `POST flags should return 400 when request validation fails due to empty description`() { val request = CreateFeatureFlagRequest( key = "new-feature", @@ -175,18 +192,20 @@ class FeatureFlagControllerTest { .content(objectMapper.writeValueAsString(request)), ) .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value("BAD_REQUEST")) + .andExpect(jsonPath("$.message").value("Description is required.")) } @Test @WithMockUser(username = "1") - fun `should return 400 when request validation fails due to invalid type`() { + fun `POST flags should return 400 when request validation fails due to invalid type`() { val request = CreateFeatureFlagRequest( key = "new-feature", status = true, type = "invalid-type", defaultValue = mapOf("default" to true), - description = "", + description = "description", ) mockMvc.perform( @@ -196,5 +215,277 @@ class FeatureFlagControllerTest { .content(objectMapper.writeValueAsString(request)), ) .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value("BAD_REQUEST")) + .andExpect(jsonPath("$.message").value("Type must be one of: number, boolean, string")) + } + + @Test + @WithMockUser(username = "1") + fun `GET flags should get all feature flags successfully`() { + `when`(featureFlagService.getFlags()).thenReturn(listOf(flag)) + + mockMvc.perform(get("/api/v1/flags")) + .andExpect(status().isOk) + .andExpect(jsonPath("$.status").value("Success")) + .andExpect(jsonPath("$.message").value("Fetched all feature flags successfully")) + .andExpect(jsonPath("$.data[0].key").value("test-flag")) + .andExpect(jsonPath("$.data[0].status").value(true)) + .andExpect(jsonPath("$.data[0].type").value("BOOLEAN")) + .andExpect(jsonPath("$.data[0].defaultValue.boolean").value(false)) + .andExpect(jsonPath("$.data[0].description").value("Test Flag")) + .andExpect(jsonPath("$.data[0].variants[0].boolean").value(false)) + .andExpect(jsonPath("$.data[0].variants[1].US").value(true)) + .andExpect(jsonPath("$.data[0].variants[2].KR").value(true)) + .andExpect(jsonPath("$.data[0].createdBy").value("username")) + .andExpect(jsonPath("$.data[0].updatedBy").value("username")) + .andExpect(jsonPath("$.data[0].createdAt").exists()) + .andExpect(jsonPath("$.data[0].updatedAt").exists()) + .andDo(print()) + } + + @Test + @WithMockUser(username = "1") + fun `GET flags by key should get a specific feature flag successfully`() { + `when`(featureFlagService.getFlagOrThrow("test-flag")).thenReturn(flag) + + mockMvc.perform(get("/api/v1/flags/test-flag")) + .andExpect(status().isOk) + .andExpect(jsonPath("$.status").value("Success")) + .andExpect(jsonPath("$.message").value("Fetched a flag successfully")) + .andExpect(jsonPath("$.data.key").value("test-flag")) + .andExpect(jsonPath("$.data.status").value(true)) + .andExpect(jsonPath("$.data.type").value("BOOLEAN")) + .andExpect(jsonPath("$.data.defaultValue.boolean").value(false)) + .andExpect(jsonPath("$.data.description").value("Test Flag")) + .andExpect(jsonPath("$.data.variants[0].boolean").value(false)) + .andExpect(jsonPath("$.data.variants[1].US").value(true)) + .andExpect(jsonPath("$.data.variants[2].KR").value(true)) + .andExpect(jsonPath("$.data.createdBy").value("username")) + .andExpect(jsonPath("$.data.updatedBy").value("username")) + .andExpect(jsonPath("$.data.createdAt").exists()) + .andExpect(jsonPath("$.data.updatedAt").exists()) + .andDo(print()) + } + + @Test + @WithMockUser(username = "1") + fun `GET flags by key return 400 when flag does not exists`() { + `when`(featureFlagService.getFlagOrThrow("test-flag")).thenThrow(EntityNotFoundException()) + + mockMvc.perform(get("/api/v1/flags/test-flag")) + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value("BAD_REQUEST")) + .andExpect(jsonPath("$.message").value("Entity not found")) + } + + @Test + @WithMockUser(username = "1") + fun `PUT flags should update a feature flag successfully`() { + val request = + UpdateFeatureFlagRequest( + key = "test-flag-updated", + type = "BOOLEAN", + description = "Updated description", + defaultValue = mapOf("default" to true), + variants = null, + ) + val updated = + FeatureFlag( + id = flag.id, + name = "test-flag-updated", + description = "Updated description", + type = flag.type, + enabled = flag.enabled, + createdBy = flag.createdBy, + updatedBy = flag.updatedBy, + defaultCondition = flag.defaultCondition, + conditions = flag.conditions, + ) + + `when`(userRepository.findById(1L)).thenReturn(Optional.of(user)) + `when`(featureFlagService.update(user, "test-flag", request)).thenReturn(updated) + + mockMvc.perform( + put("/api/v1/flags/test-flag") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)), + ) + .andExpect(status().isOk) + .andExpect(jsonPath("$.status").value("Success")) + .andExpect(jsonPath("$.message").value("Updated flag successfully")) + .andExpect(jsonPath("$.data.key").value("test-flag-updated")) + .andExpect(jsonPath("$.data.description").value("Updated description")) + .andExpect(jsonPath("$.data.status").value(true)) + .andExpect(jsonPath("$.data.type").value("BOOLEAN")) + .andExpect(jsonPath("$.data.defaultValue.boolean").value(false)) + .andExpect(jsonPath("$.data.description").value("Updated description")) + .andExpect(jsonPath("$.data.variants[0].boolean").value(false)) + .andExpect(jsonPath("$.data.variants[1].US").value(true)) + .andExpect(jsonPath("$.data.variants[2].KR").value(true)) + .andExpect(jsonPath("$.data.createdBy").value("username")) + .andExpect(jsonPath("$.data.updatedBy").value("username")) + .andExpect(jsonPath("$.data.createdAt").exists()) + .andExpect(jsonPath("$.data.updatedAt").exists()) + .andDo(print()) + } + + @Test + @WithMockUser(username = "1") + fun `PUT flags return 400 when flag does not exists`() { + val request = + UpdateFeatureFlagRequest( + key = "test-flag-updated", + type = "BOOLEAN", + description = "Updated description", + defaultValue = mapOf("default" to true), + variants = null, + ) + + `when`(userRepository.findById(1L)).thenReturn(Optional.of(user)) + `when`(featureFlagService.update(user, "test-flag", request)).thenThrow(EntityNotFoundException()) + + mockMvc.perform( + put("/api/v1/flags/test-flag") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)), + ) + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value("BAD_REQUEST")) + .andExpect(jsonPath("$.message").value("Entity not found")) + } + + @Test + @WithMockUser(username = "1") + fun `PUT flags should return 400 when request validation fails due to blank key`() { + val request = + UpdateFeatureFlagRequest( + key = "", + type = "BOOLEAN", + description = "Updated description", + defaultValue = mapOf("default" to true), + variants = null, + ) + + `when`(userRepository.findById(1L)).thenReturn(Optional.of(user)) + + mockMvc.perform( + put("/api/v1/flags/test-flag") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)), + ) + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value("BAD_REQUEST")) + .andExpect(jsonPath("$.message").value("Key is required.")) + } + + @Test + @WithMockUser(username = "1") + fun `PUT flags should return 400 when request validation fails due to invalid type`() { + val request = + UpdateFeatureFlagRequest( + key = "test-flag", + type = "INVALID_TYPE", + description = "Updated description", + defaultValue = mapOf("default" to true), + variants = null, + ) + + `when`(userRepository.findById(1L)).thenReturn(Optional.of(user)) + + mockMvc.perform( + put("/api/v1/flags/test-flag") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)), + ) + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value("BAD_REQUEST")) + .andExpect(jsonPath("$.message").value("Type must be one of: number, boolean, string")) + } + + @Test + @WithMockUser(username = "1") + fun `PUT flags should return 400 when request validation fails due to empty defaultValue`() { + val request = + UpdateFeatureFlagRequest( + key = "test-flag", + type = "BOOLEAN", + description = "Updated description", + defaultValue = emptyMap(), + variants = null, + ) + + `when`(userRepository.findById(1L)).thenReturn(Optional.of(user)) + + mockMvc.perform( + put("/api/v1/flags/test-flag") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)), + ) + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value("BAD_REQUEST")) + .andExpect(jsonPath("$.message").value("Default value is required.")) + } + + @Test + @WithMockUser(username = "1") + fun `PUT flags should return 400 when request validation fails due to blank description`() { + val request = + UpdateFeatureFlagRequest( + key = "test-flag", + type = "BOOLEAN", + description = "", + defaultValue = mapOf("default" to true), + variants = null, + ) + + `when`(userRepository.findById(1L)).thenReturn(Optional.of(user)) + + mockMvc.perform( + put("/api/v1/flags/test-flag") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)), + ) + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value("BAD_REQUEST")) + .andExpect(jsonPath("$.message").value("Description is required.")) + } + + @Test + @WithMockUser(username = "1") + fun `PATCH flags should update feature flag status successfully`() { + `when`(userRepository.findById(1L)).thenReturn(Optional.of(user)) + doNothing().`when`(featureFlagService).update(user, "test-flag", false) + + mockMvc.perform( + patch("/api/v1/flags/test-flag?status=false") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON), + ) + .andExpect(status().isOk) + .andExpect(jsonPath("$.status").value("Success")) + .andExpect(jsonPath("$.message").value("Successfully updated the status")) + .andDo(print()) + } + + @Test + @WithMockUser(username = "1") + fun `PATCH flags return 400 when flag does not exists`() { + `when`(userRepository.findById(1L)).thenReturn(Optional.of(user)) + `when`(featureFlagService.update(user, "test-flag", false)).thenThrow(EntityNotFoundException()) + + mockMvc.perform( + patch("/api/v1/flags/test-flag?status=false") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON), + ) + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value("BAD_REQUEST")) + .andExpect(jsonPath("$.message").value("Entity not found")) } } From 55e205cd241e36249ddb093a3d094d67860b6946 Mon Sep 17 00:00:00 2001 From: Nayeon Kim Date: Sun, 2 Mar 2025 20:08:11 +0900 Subject: [PATCH 5/5] fix: Set updatedBy when update flag status --- .../com/lightswitch/application/service/FeatureFlagService.kt | 1 + .../lightswitch/application/service/FeatureFlagServiceTest.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/backend/src/main/kotlin/com/lightswitch/application/service/FeatureFlagService.kt b/backend/src/main/kotlin/com/lightswitch/application/service/FeatureFlagService.kt index a1947a5..cf6c26e 100644 --- a/backend/src/main/kotlin/com/lightswitch/application/service/FeatureFlagService.kt +++ b/backend/src/main/kotlin/com/lightswitch/application/service/FeatureFlagService.kt @@ -92,6 +92,7 @@ class FeatureFlagService( ) { val flag = getFlagOrThrow(key) flag.enabled = enabled + flag.updatedBy = user featureFlagRepository.save(flag) } } diff --git a/backend/src/test/kotlin/com/lightswitch/application/service/FeatureFlagServiceTest.kt b/backend/src/test/kotlin/com/lightswitch/application/service/FeatureFlagServiceTest.kt index b65563f..5b14c59 100644 --- a/backend/src/test/kotlin/com/lightswitch/application/service/FeatureFlagServiceTest.kt +++ b/backend/src/test/kotlin/com/lightswitch/application/service/FeatureFlagServiceTest.kt @@ -474,6 +474,7 @@ class FeatureFlagServiceTest : BaseRepositoryTest() { val updatedFlag = featureFlagRepository.findById(flag.id!!.toInt()).orElseThrow() assertThat(updatedFlag.enabled).isTrue() + assertThat(updatedFlag.updatedBy).isEqualTo(user) } @Test