Ergänzt Auftragserfassung und -bearbeitung, Unternehmerbelege, Stornos, Lagerkorrekturen, Stammdaten, Abrechnung, CRM, Formulare, Berichte und Preisverwaltung auf Basis der aktuellen PHP-Anwendung. Enthält Integrations- und Oberflächentests sowie dokumentierte Abgleiche und Prüfstände.
86 lines
6.5 KiB
Java
86 lines
6.5 KiB
Java
package de.stadtbote.votian;
|
|
|
|
import de.stadtbote.votian.database.DatabaseService;
|
|
import org.junit.jupiter.api.*;
|
|
import org.springframework.security.access.AccessDeniedException;
|
|
import java.time.*;
|
|
import java.util.*;
|
|
import java.util.function.Supplier;
|
|
import static org.assertj.core.api.Assertions.*;
|
|
import static org.mockito.Mockito.*;
|
|
import static org.mockito.ArgumentMatchers.*;
|
|
import static de.stadtbote.votian.OrderEntryService.fields;
|
|
import static de.stadtbote.votian.CalendarAdministration.*;
|
|
|
|
class CalendarAdministrationTest {
|
|
final DatabaseService db=mock(DatabaseService.class);
|
|
final LegacyUser user=new LegacyUser(11,12,7,"test","1".repeat(70));
|
|
final CalendarAdministration service=new CalendarAdministration(db,Clock.fixed(Instant.parse("2026-10-12T09:00:00Z"),ZoneId.of("Europe/Berlin")));
|
|
final List<Map<String,Object>> holidays=new ArrayList<>();
|
|
final Map<String,Object> travel=fields("id",1L,"postalCode","10115","country","DE","globalMinutes",30,"localMinutes",null);
|
|
@BeforeEach void setup(){
|
|
when(db.readTransaction(any())).thenAnswer(c->((Supplier<?>)c.getArgument(0)).get());
|
|
when(db.writeTransaction(any())).thenAnswer(c->((Supplier<?>)c.getArgument(0)).get());
|
|
when(db.lockRows(anyString(),any(Object[].class))).thenAnswer(c->((String)c.getArgument(0)).startsWith("SELECT ph_id")?holidays:List.of(fields("hq_id",7)));
|
|
when(db.queryForList(anyString(),any(Object[].class))).thenAnswer(c->{String sql=c.getArgument(0);if(sql.startsWith("SELECT ph_id"))return holidays;if(sql.startsWith("SELECT p.srvp_id"))return List.of(travel);return List.of();});
|
|
when(db.update(anyString(),any(Object[].class))).thenReturn(1);
|
|
}
|
|
@Test void rightsAreCheckedBeforeAnyDatabaseAccess(){
|
|
var denied=new LegacyUser(11,12,7,"test","0".repeat(70));
|
|
assertThatThrownBy(()->service.holidays(denied,2026)).isInstanceOf(AccessDeniedException.class);
|
|
assertThatThrownBy(()->service.saveTravelTime(denied,TravelMode.BWV,1,new TravelSave("x",20))).isInstanceOf(AccessDeniedException.class);
|
|
verifyNoInteractions(db);
|
|
}
|
|
@Test void historicalRowsAndTimeWindowsSurviveAnEmptyFutureCalendar(){
|
|
holidays.add(fields("ph_id",1,"ph_month",1,"ph_day",1,"ph_name","Neujahr","ph_time_from","10:00","ph_time_to","12:00"));
|
|
holidays.add(fields("ph_id",2,"ph_month",12,"ph_day",25,"ph_name","Weihnachten","ph_time_from","00:00","ph_time_to","00:00"));
|
|
service.saveHolidays(user,2026,new HolidaySave(AdminSupport.revision(holidays),List.of()));
|
|
verify(db).update("DELETE FROM publicholiday WHERE ph_id=? AND hq_id=?",2L,7L);
|
|
verify(db,never()).update(eq("DELETE FROM publicholiday WHERE ph_id=? AND hq_id=?"),eq(1L),any());
|
|
}
|
|
@Test void staleCalendarCannotOverwriteAnotherEditorsChanges(){
|
|
assertThatThrownBy(()->service.saveHolidays(user,2026,new HolidaySave("outdated",List.of()))).hasMessageContaining("409");
|
|
verify(db,never()).update(anyString(),any(Object[].class));verify(db,never()).insert(anyString(),anyMap());
|
|
}
|
|
@Test void validatesDateAndRejectsModificationOfToday(){
|
|
assertThatThrownBy(()->service.saveHolidays(user,2026,new HolidaySave("x",List.of(new Holiday(2,29,"x"))))).hasMessageContaining("Feiertagsdatum");
|
|
assertThatThrownBy(()->service.saveHolidays(user,2026,new HolidaySave(AdminSupport.revision(holidays),List.of(new Holiday(10,12,"x"))))).hasMessageContaining("heute");
|
|
verify(db,never()).insert(anyString(),anyMap());
|
|
}
|
|
@Test void futureNamesAreUpdatedWithoutReplacingTimeWindows(){
|
|
holidays.add(fields("ph_id",2,"ph_month",12,"ph_day",25,"ph_name","alt","ph_time_from","10:00","ph_time_to","12:00"));
|
|
service.saveHolidays(user,2026,new HolidaySave(AdminSupport.revision(holidays),List.of(new Holiday(12,25,"neu"))));
|
|
verify(db).update("UPDATE publicholiday SET ph_name=? WHERE ph_id=? AND hq_id=?","neu",2L,7L);
|
|
verify(db,never()).insert(anyString(),anyMap());
|
|
}
|
|
@Test void multipleTimeWindowsRepresentOneDayAndSurviveAnUnchangedSave(){
|
|
holidays.add(fields("ph_id",2,"ph_month",12,"ph_day",25,"ph_name","vormittags","ph_time_from","10:00","ph_time_to","12:00"));
|
|
holidays.add(fields("ph_id",3,"ph_month",12,"ph_day",25,"ph_name","nachmittags","ph_time_from","14:00","ph_time_to","16:00"));
|
|
assertThat((List<?>)service.holidays(user,2026).get("days")).hasSize(1);
|
|
service.saveHolidays(user,2026,new HolidaySave(AdminSupport.revision(holidays),List.of(new Holiday(12,25,"vormittags"))));
|
|
verify(db,never()).update(anyString(),any(Object[].class));
|
|
}
|
|
@Test void classicCreatesOnlyThisHeadquartersOverride(){
|
|
service.saveTravelTime(user,TravelMode.CLASSIC,1,new TravelSave(AdminSupport.revision(Arrays.asList(1L,TravelMode.CLASSIC,null)),45));
|
|
verify(db).update("INSERT INTO serviceplztraveltime (srvp_id,hq_id,srvpt_traveltime) VALUES (?,?,?)",1L,7L,45);
|
|
verify(db,never()).update(startsWith("UPDATE serviceplz SET"),any(Object[].class));
|
|
}
|
|
@Test void bwvIsOneConditionalWriteAndReportsConcurrentUpdates(){
|
|
when(db.update(startsWith("UPDATE serviceplz SET"),any(Object[].class))).thenReturn(0);
|
|
assertThatThrownBy(()->service.saveTravelTime(user,TravelMode.BWV,1,new TravelSave(AdminSupport.revision(Arrays.asList(1L,TravelMode.BWV,30)),45))).hasMessageContaining("409");
|
|
verify(db).update("UPDATE serviceplz SET srvp_traveltime=? WHERE srvp_id=? AND srvp_valid=1 AND srvp_traveltime <=> ?",45,1L,30);
|
|
verify(db,never()).insert(anyString(),anyMap());
|
|
}
|
|
@Test void zeroAndEmptyTravelTimeUseTheConfiguredHeadquartersDefault() {
|
|
when(db.queryForList(startsWith("SELECT par_value FROM parameter"),eq(7L),eq("LATEST_TAKETIME_IN_MINUTES"))).thenReturn(List.of(fields("par_value","35")));
|
|
service.saveTravelTime(user,TravelMode.CLASSIC,1,new TravelSave(AdminSupport.revision(Arrays.asList(1L,TravelMode.CLASSIC,null)),0));
|
|
verify(db).update("INSERT INTO serviceplztraveltime (srvp_id,hq_id,srvpt_traveltime) VALUES (?,?,?)",1L,7L,35);
|
|
service.saveTravelTime(user,TravelMode.BWV,1,new TravelSave(AdminSupport.revision(Arrays.asList(1L,TravelMode.BWV,30)),null));
|
|
verify(db).update(startsWith("UPDATE serviceplz SET"),eq(35),eq(1L),eq(30));
|
|
}
|
|
@Test void missingDefaultNeverWritesAnInventedTravelTime() {
|
|
assertThatThrownBy(()->service.saveTravelTime(user,TravelMode.CLASSIC,1,new TravelSave("",0))).hasMessageContaining("LATEST_TAKETIME_IN_MINUTES");
|
|
verify(db,never()).update(anyString(),any(Object[].class));
|
|
}
|
|
}
|