Files
kst4contest/src/test/java/kst4contest/view/OperatorProfileBootstrapTest.java
T
Claude eeee11e4e7 Select the operator profile at startup
Resolves the active operator profile before the chat controller is built and
passes its two file names on, so preferences, layout and worked data follow the
profile.

The resolution is deliberately quiet for existing installations. With no
registry or exactly one profile nothing is asked and nothing is written, so a
single operator start is unchanged. Only from two profiles on does a small
picker appear with the last used profile preselected, where Enter or a double
click starts immediately. A "--profile" argument, or the equivalent system
property, skips the picker; an unknown name warns and falls back to the normal
selection instead of refusing to start.

The startup decision itself lives in OperatorProfileBootstrap and contains no
user interface code, so it is covered by headless tests. The window title gains
the profile name only when a second profile exists.

Command line parsing happens in init() and is kept in a process wide holder,
because JavaFX only knows the parameters of the instance it launched itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hpa6bjie5qkeNG62y6FmXm
2026-09-07 07:44:22 +00:00

162 lines
6.2 KiB
Java

package kst4contest.view;
import kst4contest.controller.OperatorProfilePaths;
import kst4contest.controller.OperatorProfileStore;
import kst4contest.model.OperatorProfile;
import kst4contest.model.OperatorProfileSelection;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
class OperatorProfileBootstrapTest {
@TempDir
Path temporaryDirectory;
@Test
void installationWithoutRegistryStartsSilentlyOnTheHistoricLayout() {
AtomicInteger pickerInvocations = new AtomicInteger();
OperatorProfileStore store = storeAt();
OperatorProfileSelection resolved = new OperatorProfileBootstrap().resolveAtStartup(
store, new CommandLineOptions(null), countingPicker(pickerInvocations, null));
assertNotNull(resolved);
assertEquals("preferences.xml", resolved.getPreferencesRelativeFileName());
assertEquals("praktiKST.db", resolved.getWorkedDatabaseRelativeFileName());
// Nothing may be asked, and nothing may be written.
assertEquals(0, pickerInvocations.get());
assertTrue(store.loadProfiles().isEmpty());
}
@Test
void singleProfileStartsWithoutAskingAnything() {
AtomicInteger pickerInvocations = new AtomicInteger();
OperatorProfileStore store = storeAt();
store.saveProfiles(List.of(OperatorProfilePaths.buildRootProfile("Default")), "default");
OperatorProfileSelection resolved = new OperatorProfileBootstrap().resolveAtStartup(
store, new CommandLineOptions(null), countingPicker(pickerInvocations, null));
assertNotNull(resolved);
assertEquals(0, pickerInvocations.get());
}
@Test
void twoProfilesAskTheOperatorAndPreselectTheLastUsedOne() {
OperatorProfileStore store = storeAt();
OperatorProfile secondProfile = new OperatorProfile("OP2", "DN9APW", false, false);
store.saveProfiles(
List.of(OperatorProfilePaths.buildRootProfile("Default"), secondProfile), "OP2");
AtomicInteger pickerInvocations = new AtomicInteger();
String[] observedPreselection = new String[1];
OperatorProfileSelection resolved = new OperatorProfileBootstrap().resolveAtStartup(
store,
new CommandLineOptions(null),
(profiles, preselectedProfileId) -> {
pickerInvocations.incrementAndGet();
observedPreselection[0] = preselectedProfileId;
return Optional.of(profiles.get(1));
});
assertEquals(1, pickerInvocations.get());
assertEquals("OP2", observedPreselection[0]);
assertNotNull(resolved);
assertEquals("profiles/OP2/praktiKST.db", resolved.getWorkedDatabaseRelativeFileName());
}
@Test
void aValidProfileArgumentSkipsThePicker() {
OperatorProfileStore store = storeAt();
store.saveProfiles(
List.of(OperatorProfilePaths.buildRootProfile("Default"),
new OperatorProfile("OP2", "DN9APW", false, false)),
"default");
AtomicInteger pickerInvocations = new AtomicInteger();
OperatorProfileBootstrap bootstrap = new OperatorProfileBootstrap();
OperatorProfileSelection byId = bootstrap.resolveAtStartup(
store, new CommandLineOptions("op2"), countingPicker(pickerInvocations, null));
assertEquals(0, pickerInvocations.get());
assertEquals("profiles/OP2/preferences.xml", byId.getPreferencesRelativeFileName());
assertNull(bootstrap.getStartupWarning());
OperatorProfileSelection byDisplayName = bootstrap.resolveAtStartup(
store, new CommandLineOptions("DN9APW"), countingPicker(pickerInvocations, null));
assertEquals(0, pickerInvocations.get());
assertEquals("profiles/OP2/preferences.xml", byDisplayName.getPreferencesRelativeFileName());
}
@Test
void anUnknownProfileArgumentWarnsAndFallsBackToTheNormalSelection() {
OperatorProfileStore store = storeAt();
store.saveProfiles(
List.of(OperatorProfilePaths.buildRootProfile("Default"),
new OperatorProfile("OP2", "DN9APW", false, false)),
"default");
AtomicInteger pickerInvocations = new AtomicInteger();
OperatorProfileBootstrap bootstrap = new OperatorProfileBootstrap();
OperatorProfileSelection resolved = bootstrap.resolveAtStartup(
store, new CommandLineOptions("NOPE"), countingPicker(pickerInvocations, 0));
assertEquals(1, pickerInvocations.get());
assertNotNull(resolved);
assertNotNull(bootstrap.getStartupWarning());
assertTrue(bootstrap.getStartupWarning().contains("NOPE"));
}
@Test
void quittingInThePickerYieldsNoSelection() {
OperatorProfileStore store = storeAt();
store.saveProfiles(
List.of(OperatorProfilePaths.buildRootProfile("Default"),
new OperatorProfile("OP2", "DN9APW", false, false)),
"default");
OperatorProfileSelection resolved = new OperatorProfileBootstrap().resolveAtStartup(
store, new CommandLineOptions(null), (profiles, preselected) -> Optional.empty());
assertNull(resolved);
}
private OperatorProfileChoiceRequester countingPicker(final AtomicInteger invocationCounter,
final Integer profileIndexToChoose) {
return (profiles, preselectedProfileId) -> {
invocationCounter.incrementAndGet();
if (profileIndexToChoose == null) {
return Optional.of(profiles.get(0));
}
return Optional.of(profiles.get(profileIndexToChoose));
};
}
private OperatorProfileStore storeAt() {
return new OperatorProfileStore(temporaryDirectory.resolve("profiles.xml").toString());
}
}