test(shift): cover non-zero cycleOffset and multiple phase breaks; add kindAt KDoc

This commit is contained in:
xfy 2026-07-06 18:20:48 +08:00
parent 778d5dc8be
commit eca6c61058
2 changed files with 39 additions and 0 deletions

View File

@ -39,6 +39,10 @@ data class ShiftPattern(
val phaseBreaks: List<PhaseBreak> = emptyList(),
val name: String = "默认"
) {
/**
* 返回 [date] 当天的班次优先级:overrides 活跃锚点的 cycle 索引
* cycle 为空时返回 null
*/
fun kindAt(date: LocalDate): ShiftKind? {
if (cycle.isEmpty()) return null
// 1. 单日翻转优先

View File

@ -217,6 +217,41 @@ class ShiftPatternTest {
assertEquals(ShiftKind.OFF, pattern.kindAt(LocalDate(2026, 5, 18)))
}
@Test
fun kindAt_phaseBreakWithOffset_shiftsPhase() {
// 断点 5/19,cycleOffset=2:5/19 对应 cycle[2]=OFF(而非 cycle[0]=WORK)
val pattern = twoOnTwoOff.copy(
phaseBreaks = listOf(PhaseBreak(LocalDate(2026, 5, 19), 2))
)
// 5/19 = cycle[(0+2)%4] = cycle[2] = OFF
assertEquals(ShiftKind.OFF, pattern.kindAt(LocalDate(2026, 5, 19)))
// 5/20 = cycle[(1+2)%4] = cycle[3] = OFF
assertEquals(ShiftKind.OFF, pattern.kindAt(LocalDate(2026, 5, 20)))
// 5/21 = cycle[(2+2)%4] = cycle[0] = WORK
assertEquals(ShiftKind.WORK, pattern.kindAt(LocalDate(2026, 5, 21)))
}
@Test
fun kindAt_multiplePhaseBreaks_usesLatestApplicable() {
// 两个断点:5/19(offset 0) 和 5/25(offset 1)
val pattern = twoOnTwoOff.copy(
phaseBreaks = listOf(
PhaseBreak(LocalDate(2026, 5, 19), 0),
PhaseBreak(LocalDate(2026, 5, 25), 1)
)
)
// 5/19-5/24 受第一个断点支配(offset 0)
// 5/19 = cycle[0] = WORK
assertEquals(ShiftKind.WORK, pattern.kindAt(LocalDate(2026, 5, 19)))
// 5/24 = cycle[(5)%4] = cycle[1] = WORK
assertEquals(ShiftKind.WORK, pattern.kindAt(LocalDate(2026, 5, 24)))
// 5/25 起受第二个断点支配(offset 1)
// 5/25 = cycle[(0+1)%4] = cycle[1] = WORK
assertEquals(ShiftKind.WORK, pattern.kindAt(LocalDate(2026, 5, 25)))
// 5/26 = cycle[(1+1)%4] = cycle[2] = OFF
assertEquals(ShiftKind.OFF, pattern.kindAt(LocalDate(2026, 5, 26)))
}
// ---- kindAt: override + phaseBreak 组合(用户原始例子)----
@Test