mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 17:39:57 +01:00
feat(inventory): Bestandsparameter ändern (MinimumLevel, MinimumShelfLife)
Stock.update(StockUpdateDraft) ermöglicht optionale Aktualisierung von
MinimumLevel und MinimumShelfLife mit identischer Validierung wie create().
PUT /api/inventory/stocks/{id} Endpoint, UpdateStock Use Case + Tests.
Closes #9
This commit is contained in:
parent
1c65ac7795
commit
e8cbb948b7
10 changed files with 734 additions and 1 deletions
|
|
@ -0,0 +1,193 @@
|
|||
package de.effigenix.application.inventory;
|
||||
|
||||
import de.effigenix.application.inventory.command.UpdateStockCommand;
|
||||
import de.effigenix.domain.inventory.*;
|
||||
import de.effigenix.shared.common.RepositoryError;
|
||||
import de.effigenix.shared.common.Result;
|
||||
import de.effigenix.shared.common.UnitOfMeasure;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("UpdateStock Use Case")
|
||||
class UpdateStockTest {
|
||||
|
||||
@Mock private StockRepository stockRepository;
|
||||
|
||||
private UpdateStock updateStock;
|
||||
private Stock existingStock;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
updateStock = new UpdateStock(stockRepository);
|
||||
|
||||
existingStock = Stock.reconstitute(
|
||||
StockId.of("stock-1"),
|
||||
de.effigenix.domain.masterdata.ArticleId.of("article-1"),
|
||||
StorageLocationId.of("location-1"),
|
||||
null, null,
|
||||
List.of()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should update minimumLevel successfully")
|
||||
void shouldUpdateMinimumLevel() {
|
||||
when(stockRepository.findById(StockId.of("stock-1")))
|
||||
.thenReturn(Result.success(Optional.of(existingStock)));
|
||||
when(stockRepository.save(any())).thenReturn(Result.success(null));
|
||||
|
||||
var cmd = new UpdateStockCommand("stock-1", "50", "KILOGRAM", null);
|
||||
var result = updateStock.execute(cmd);
|
||||
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
var stock = result.unsafeGetValue();
|
||||
assertThat(stock.minimumLevel()).isNotNull();
|
||||
assertThat(stock.minimumLevel().quantity().amount().intValue()).isEqualTo(50);
|
||||
assertThat(stock.minimumLevel().quantity().uom()).isEqualTo(UnitOfMeasure.KILOGRAM);
|
||||
assertThat(stock.minimumShelfLife()).isNull();
|
||||
verify(stockRepository).save(existingStock);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should update minimumShelfLife successfully")
|
||||
void shouldUpdateMinimumShelfLife() {
|
||||
when(stockRepository.findById(StockId.of("stock-1")))
|
||||
.thenReturn(Result.success(Optional.of(existingStock)));
|
||||
when(stockRepository.save(any())).thenReturn(Result.success(null));
|
||||
|
||||
var cmd = new UpdateStockCommand("stock-1", null, null, 30);
|
||||
var result = updateStock.execute(cmd);
|
||||
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
var stock = result.unsafeGetValue();
|
||||
assertThat(stock.minimumShelfLife()).isNotNull();
|
||||
assertThat(stock.minimumShelfLife().days()).isEqualTo(30);
|
||||
assertThat(stock.minimumLevel()).isNull();
|
||||
verify(stockRepository).save(existingStock);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should update both parameters at once")
|
||||
void shouldUpdateBothParameters() {
|
||||
when(stockRepository.findById(StockId.of("stock-1")))
|
||||
.thenReturn(Result.success(Optional.of(existingStock)));
|
||||
when(stockRepository.save(any())).thenReturn(Result.success(null));
|
||||
|
||||
var cmd = new UpdateStockCommand("stock-1", "100", "LITER", 14);
|
||||
var result = updateStock.execute(cmd);
|
||||
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
var stock = result.unsafeGetValue();
|
||||
assertThat(stock.minimumLevel()).isNotNull();
|
||||
assertThat(stock.minimumLevel().quantity().uom()).isEqualTo(UnitOfMeasure.LITER);
|
||||
assertThat(stock.minimumShelfLife()).isNotNull();
|
||||
assertThat(stock.minimumShelfLife().days()).isEqualTo(14);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should not change fields when null in command")
|
||||
void shouldNotChangeFieldsWhenNull() {
|
||||
var stockWithParams = Stock.reconstitute(
|
||||
StockId.of("stock-1"),
|
||||
de.effigenix.domain.masterdata.ArticleId.of("article-1"),
|
||||
StorageLocationId.of("location-1"),
|
||||
MinimumLevel.of("25", "KILOGRAM").unsafeGetValue(),
|
||||
MinimumShelfLife.of(7).unsafeGetValue(),
|
||||
List.of()
|
||||
);
|
||||
when(stockRepository.findById(StockId.of("stock-1")))
|
||||
.thenReturn(Result.success(Optional.of(stockWithParams)));
|
||||
when(stockRepository.save(any())).thenReturn(Result.success(null));
|
||||
|
||||
var cmd = new UpdateStockCommand("stock-1", null, null, null);
|
||||
var result = updateStock.execute(cmd);
|
||||
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
var stock = result.unsafeGetValue();
|
||||
assertThat(stock.minimumLevel().quantity().amount().intValue()).isEqualTo(25);
|
||||
assertThat(stock.minimumShelfLife().days()).isEqualTo(7);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should fail with StockNotFound when stock does not exist")
|
||||
void shouldFailWhenStockNotFound() {
|
||||
when(stockRepository.findById(StockId.of("stock-1")))
|
||||
.thenReturn(Result.success(Optional.empty()));
|
||||
|
||||
var cmd = new UpdateStockCommand("stock-1", "50", "KILOGRAM", null);
|
||||
var result = updateStock.execute(cmd);
|
||||
|
||||
assertThat(result.isFailure()).isTrue();
|
||||
assertThat(result.unsafeGetError()).isInstanceOf(StockError.StockNotFound.class);
|
||||
verify(stockRepository, never()).save(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should fail with InvalidMinimumLevel for invalid amount")
|
||||
void shouldFailForInvalidMinimumLevel() {
|
||||
when(stockRepository.findById(StockId.of("stock-1")))
|
||||
.thenReturn(Result.success(Optional.of(existingStock)));
|
||||
|
||||
var cmd = new UpdateStockCommand("stock-1", "-5", "KILOGRAM", null);
|
||||
var result = updateStock.execute(cmd);
|
||||
|
||||
assertThat(result.isFailure()).isTrue();
|
||||
assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidMinimumLevel.class);
|
||||
verify(stockRepository, never()).save(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should fail with InvalidMinimumShelfLife for zero days")
|
||||
void shouldFailForInvalidMinimumShelfLife() {
|
||||
when(stockRepository.findById(StockId.of("stock-1")))
|
||||
.thenReturn(Result.success(Optional.of(existingStock)));
|
||||
|
||||
var cmd = new UpdateStockCommand("stock-1", null, null, 0);
|
||||
var result = updateStock.execute(cmd);
|
||||
|
||||
assertThat(result.isFailure()).isTrue();
|
||||
assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidMinimumShelfLife.class);
|
||||
verify(stockRepository, never()).save(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should fail with RepositoryFailure when findById fails")
|
||||
void shouldFailWhenFindByIdFails() {
|
||||
when(stockRepository.findById(StockId.of("stock-1")))
|
||||
.thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));
|
||||
|
||||
var cmd = new UpdateStockCommand("stock-1", "50", "KILOGRAM", null);
|
||||
var result = updateStock.execute(cmd);
|
||||
|
||||
assertThat(result.isFailure()).isTrue();
|
||||
assertThat(result.unsafeGetError()).isInstanceOf(StockError.RepositoryFailure.class);
|
||||
verify(stockRepository, never()).save(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should fail with RepositoryFailure when save fails")
|
||||
void shouldFailWhenSaveFails() {
|
||||
when(stockRepository.findById(StockId.of("stock-1")))
|
||||
.thenReturn(Result.success(Optional.of(existingStock)));
|
||||
when(stockRepository.save(any()))
|
||||
.thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));
|
||||
|
||||
var cmd = new UpdateStockCommand("stock-1", "50", "KILOGRAM", null);
|
||||
var result = updateStock.execute(cmd);
|
||||
|
||||
assertThat(result.isFailure()).isTrue();
|
||||
assertThat(result.unsafeGetError()).isInstanceOf(StockError.RepositoryFailure.class);
|
||||
}
|
||||
}
|
||||
|
|
@ -192,6 +192,222 @@ class StockTest {
|
|||
}
|
||||
}
|
||||
|
||||
// ==================== Update ====================
|
||||
|
||||
@Nested
|
||||
@DisplayName("update()")
|
||||
class Update {
|
||||
|
||||
@Test
|
||||
@DisplayName("should update minimumLevel")
|
||||
void shouldUpdateMinimumLevel() {
|
||||
var stock = createValidStock();
|
||||
var draft = new StockUpdateDraft("20", "LITER", null);
|
||||
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
assertThat(stock.minimumLevel().quantity().amount()).isEqualByComparingTo(new BigDecimal("20"));
|
||||
assertThat(stock.minimumLevel().quantity().uom()).isEqualTo(UnitOfMeasure.LITER);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should update minimumShelfLife")
|
||||
void shouldUpdateMinimumShelfLife() {
|
||||
var stock = createValidStock();
|
||||
var draft = new StockUpdateDraft(null, null, 14);
|
||||
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
assertThat(stock.minimumShelfLife().days()).isEqualTo(14);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should update both parameters")
|
||||
void shouldUpdateBothParameters() {
|
||||
var stock = createValidStock();
|
||||
var draft = new StockUpdateDraft("5", "PIECE", 7);
|
||||
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
assertThat(stock.minimumLevel().quantity().amount()).isEqualByComparingTo(new BigDecimal("5"));
|
||||
assertThat(stock.minimumLevel().quantity().uom()).isEqualTo(UnitOfMeasure.PIECE);
|
||||
assertThat(stock.minimumShelfLife().days()).isEqualTo(7);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should not change fields when null")
|
||||
void shouldNotChangeFieldsWhenNull() {
|
||||
var stock = createValidStock(); // has minimumLevel=10 KILOGRAM, minimumShelfLife=30
|
||||
var originalLevel = stock.minimumLevel();
|
||||
var originalShelfLife = stock.minimumShelfLife();
|
||||
|
||||
var draft = new StockUpdateDraft(null, null, null);
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
assertThat(stock.minimumLevel()).isSameAs(originalLevel);
|
||||
assertThat(stock.minimumShelfLife()).isSameAs(originalShelfLife);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should overwrite existing minimumLevel with new value")
|
||||
void shouldOverwriteExistingMinimumLevel() {
|
||||
var stock = createValidStock(); // has minimumLevel=10 KILOGRAM
|
||||
var draft = new StockUpdateDraft("99.9", "GRAM", null);
|
||||
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
assertThat(stock.minimumLevel().quantity().amount()).isEqualByComparingTo(new BigDecimal("99.9"));
|
||||
assertThat(stock.minimumLevel().quantity().uom()).isEqualTo(UnitOfMeasure.GRAM);
|
||||
// minimumShelfLife unverändert
|
||||
assertThat(stock.minimumShelfLife().days()).isEqualTo(30);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should accept minimumLevel amount of zero")
|
||||
void shouldAcceptMinimumLevelZero() {
|
||||
var stock = createValidStock();
|
||||
var draft = new StockUpdateDraft("0", "KILOGRAM", null);
|
||||
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
assertThat(stock.minimumLevel().quantity().amount()).isEqualByComparingTo(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should fail when minimumLevel amount is negative")
|
||||
void shouldFailWhenMinimumLevelNegative() {
|
||||
var stock = createValidStock();
|
||||
var draft = new StockUpdateDraft("-1", "KILOGRAM", null);
|
||||
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isFailure()).isTrue();
|
||||
assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidMinimumLevel.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should fail when minimumLevel amount is not a number")
|
||||
void shouldFailWhenMinimumLevelNotNumber() {
|
||||
var stock = createValidStock();
|
||||
var draft = new StockUpdateDraft("abc", "KILOGRAM", null);
|
||||
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isFailure()).isTrue();
|
||||
assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidMinimumLevel.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should fail when minimumLevel unit is invalid")
|
||||
void shouldFailWhenMinimumLevelUnitInvalid() {
|
||||
var stock = createValidStock();
|
||||
var draft = new StockUpdateDraft("10", "INVALID_UNIT", null);
|
||||
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isFailure()).isTrue();
|
||||
assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidMinimumLevel.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should fail when minimumLevel amount provided without unit")
|
||||
void shouldFailWhenAmountWithoutUnit() {
|
||||
var stock = createValidStock();
|
||||
var draft = new StockUpdateDraft("10", null, null);
|
||||
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isFailure()).isTrue();
|
||||
assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidMinimumLevel.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should fail when minimumLevel unit provided without amount")
|
||||
void shouldFailWhenUnitWithoutAmount() {
|
||||
var stock = createValidStock();
|
||||
var draft = new StockUpdateDraft(null, "KILOGRAM", null);
|
||||
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isFailure()).isTrue();
|
||||
assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidMinimumLevel.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should fail when minimumShelfLife is zero")
|
||||
void shouldFailWhenMinimumShelfLifeZero() {
|
||||
var stock = createValidStock();
|
||||
var draft = new StockUpdateDraft(null, null, 0);
|
||||
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isFailure()).isTrue();
|
||||
assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidMinimumShelfLife.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should fail when minimumShelfLife is negative")
|
||||
void shouldFailWhenMinimumShelfLifeNegative() {
|
||||
var stock = createValidStock();
|
||||
var draft = new StockUpdateDraft(null, null, -5);
|
||||
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isFailure()).isTrue();
|
||||
assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidMinimumShelfLife.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should accept minimumShelfLife of 1")
|
||||
void shouldAcceptMinimumShelfLifeOne() {
|
||||
var stock = createValidStock();
|
||||
var draft = new StockUpdateDraft(null, null, 1);
|
||||
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
assertThat(stock.minimumShelfLife().days()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should not rollback minimumLevel when minimumShelfLife validation fails")
|
||||
void shouldNotRollbackMinimumLevelOnShelfLifeFailure() {
|
||||
var stock = createValidStock();
|
||||
var originalLevel = stock.minimumLevel();
|
||||
var draft = new StockUpdateDraft("50", "KILOGRAM", 0); // valid level, invalid shelfLife
|
||||
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isFailure()).isTrue();
|
||||
assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidMinimumShelfLife.class);
|
||||
// NOTE: minimumLevel was already mutated – this is consistent with how create() works
|
||||
// (fail-fast, no rollback). The use case should not save on failure.
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should update stock that had no optional fields")
|
||||
void shouldUpdateStockWithoutOptionalFields() {
|
||||
var stock = Stock.create(new StockDraft("article-1", "location-1", null, null, null)).unsafeGetValue();
|
||||
assertThat(stock.minimumLevel()).isNull();
|
||||
assertThat(stock.minimumShelfLife()).isNull();
|
||||
|
||||
var draft = new StockUpdateDraft("15", "KILOGRAM", 10);
|
||||
var result = stock.update(draft);
|
||||
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
assertThat(stock.minimumLevel()).isNotNull();
|
||||
assertThat(stock.minimumLevel().quantity().amount()).isEqualByComparingTo(new BigDecimal("15"));
|
||||
assertThat(stock.minimumShelfLife()).isNotNull();
|
||||
assertThat(stock.minimumShelfLife().days()).isEqualTo(10);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== addBatch ====================
|
||||
|
||||
@Nested
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import java.util.UUID;
|
|||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
/**
|
||||
|
|
@ -206,6 +207,180 @@ class StockControllerIntegrationTest extends AbstractIntegrationTest {
|
|||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
// ==================== Bestandsparameter ändern (updateStock) ====================
|
||||
|
||||
@Nested
|
||||
@DisplayName("PUT /{id} – Bestandsparameter ändern")
|
||||
class UpdateStockEndpoint {
|
||||
|
||||
@Test
|
||||
@DisplayName("MinimumLevel ändern → 200")
|
||||
void updateStock_minimumLevel_returns200() throws Exception {
|
||||
String stockId = createStock();
|
||||
|
||||
mockMvc.perform(put("/api/inventory/stocks/{id}", stockId)
|
||||
.header("Authorization", "Bearer " + adminToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"minimumLevelAmount": "50", "minimumLevelUnit": "KILOGRAM"}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id").value(stockId))
|
||||
.andExpect(jsonPath("$.minimumLevel.amount").value(50))
|
||||
.andExpect(jsonPath("$.minimumLevel.unit").value("KILOGRAM"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("MinimumShelfLife ändern → 200")
|
||||
void updateStock_minimumShelfLife_returns200() throws Exception {
|
||||
String stockId = createStock();
|
||||
|
||||
mockMvc.perform(put("/api/inventory/stocks/{id}", stockId)
|
||||
.header("Authorization", "Bearer " + adminToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"minimumShelfLifeDays": 14}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id").value(stockId))
|
||||
.andExpect(jsonPath("$.minimumShelfLifeDays").value(14));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Beide Parameter gleichzeitig ändern → 200")
|
||||
void updateStock_bothParameters_returns200() throws Exception {
|
||||
String stockId = createStock();
|
||||
|
||||
mockMvc.perform(put("/api/inventory/stocks/{id}", stockId)
|
||||
.header("Authorization", "Bearer " + adminToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"minimumLevelAmount": "25.5", "minimumLevelUnit": "LITER", "minimumShelfLifeDays": 7}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.minimumLevel.amount").value(25.5))
|
||||
.andExpect(jsonPath("$.minimumLevel.unit").value("LITER"))
|
||||
.andExpect(jsonPath("$.minimumShelfLifeDays").value(7));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Keine Parameter ändern (leerer Body) → 200 ohne Änderung")
|
||||
void updateStock_noChanges_returns200() throws Exception {
|
||||
String stockId = createStock();
|
||||
|
||||
mockMvc.perform(put("/api/inventory/stocks/{id}", stockId)
|
||||
.header("Authorization", "Bearer " + adminToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{}"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id").value(stockId));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Persistenz: geänderte Werte beim erneuten Laden sichtbar")
|
||||
void updateStock_persistsChanges() throws Exception {
|
||||
String stockId = createStock();
|
||||
|
||||
mockMvc.perform(put("/api/inventory/stocks/{id}", stockId)
|
||||
.header("Authorization", "Bearer " + adminToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"minimumLevelAmount": "42", "minimumLevelUnit": "PIECE", "minimumShelfLifeDays": 21}
|
||||
"""))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
mockMvc.perform(get("/api/inventory/stocks/{id}", stockId)
|
||||
.header("Authorization", "Bearer " + adminToken))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.minimumLevel.amount").value(42))
|
||||
.andExpect(jsonPath("$.minimumLevel.unit").value("PIECE"))
|
||||
.andExpect(jsonPath("$.minimumShelfLifeDays").value(21));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Ungültiger MinimumLevel (negativer Amount) → 400")
|
||||
void updateStock_invalidMinimumLevel_returns400() throws Exception {
|
||||
String stockId = createStock();
|
||||
|
||||
mockMvc.perform(put("/api/inventory/stocks/{id}", stockId)
|
||||
.header("Authorization", "Bearer " + adminToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"minimumLevelAmount": "-1", "minimumLevelUnit": "KILOGRAM"}
|
||||
"""))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.code").value("INVALID_MINIMUM_LEVEL"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Ungültige Unit → 400")
|
||||
void updateStock_invalidUnit_returns400() throws Exception {
|
||||
String stockId = createStock();
|
||||
|
||||
mockMvc.perform(put("/api/inventory/stocks/{id}", stockId)
|
||||
.header("Authorization", "Bearer " + adminToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"minimumLevelAmount": "10", "minimumLevelUnit": "INVALID_UNIT"}
|
||||
"""))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.code").value("INVALID_MINIMUM_LEVEL"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Ungültige MinimumShelfLife (0) → 400")
|
||||
void updateStock_invalidShelfLife_returns400() throws Exception {
|
||||
String stockId = createStock();
|
||||
|
||||
mockMvc.perform(put("/api/inventory/stocks/{id}", stockId)
|
||||
.header("Authorization", "Bearer " + adminToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"minimumShelfLifeDays": 0}
|
||||
"""))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.code").value("INVALID_MINIMUM_SHELF_LIFE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Nicht existierende Bestandsposition → 404")
|
||||
void updateStock_notFound_returns404() throws Exception {
|
||||
mockMvc.perform(put("/api/inventory/stocks/{id}", UUID.randomUUID().toString())
|
||||
.header("Authorization", "Bearer " + adminToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"minimumLevelAmount": "10", "minimumLevelUnit": "KILOGRAM"}
|
||||
"""))
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(jsonPath("$.code").value("STOCK_NOT_FOUND"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Bestandsparameter ändern ohne STOCK_WRITE → 403")
|
||||
void updateStock_withViewerToken_returns403() throws Exception {
|
||||
String stockId = createStock();
|
||||
|
||||
mockMvc.perform(put("/api/inventory/stocks/{id}", stockId)
|
||||
.header("Authorization", "Bearer " + viewerToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"minimumLevelAmount": "10", "minimumLevelUnit": "KILOGRAM"}
|
||||
"""))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Bestandsparameter ändern ohne Token → 401")
|
||||
void updateStock_withoutToken_returns401() throws Exception {
|
||||
mockMvc.perform(put("/api/inventory/stocks/{id}", UUID.randomUUID().toString())
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"minimumLevelAmount": "10", "minimumLevelUnit": "KILOGRAM"}
|
||||
"""))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Charge einbuchen (addBatch) ====================
|
||||
|
||||
@Nested
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue