- VPN retiré : le contrôle reste activé (alerte parent, tunnel remonté après 30 s, autorisation redemandée à l'ouverture) - Horloge de référence : changer date/heure/fuseau ne contourne plus les plages horaires ni le temps d'écran (signalé au parent) - Mot de passe : PBKDF2 + attente croissante après 3 échecs ; conversion automatique des anciens hachages - Écrans protégés : aucun affichage avant chargement des réglages ; effacement du journal protégé - Signature release via keystore.properties (même clé que les versions publiées) - Sauvegarde Google : réglages, appairage et journal exclus - Alerte admin fiable (goAsync), relance refusée signalée, liste d'applis renvoyée à chaque installation, code d'appairage consommé - Journal thread-safe et sans recopie à chaque ligne ; correctifs lint - Tests unitaires (mot de passe, règles, versions, config Firebase) ; README aligné sur le comportement réel Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9CBCVP5oDu2sfoYPisNCQ
65 lines
2.5 KiB
Kotlin
65 lines
2.5 KiB
Kotlin
package com.jttools.datacontrol.guard
|
|
|
|
import org.junit.Assert.assertEquals
|
|
import org.junit.Assert.assertFalse
|
|
import org.junit.Assert.assertTrue
|
|
import org.junit.Test
|
|
import java.time.LocalDateTime
|
|
|
|
class RulesTest {
|
|
|
|
// 2026-10-26 est un lundi.
|
|
private fun at(day: Int, h: Int, m: Int = 0) = LocalDateTime.of(2026, 10, 25, h, m).plusDays(day.toLong())
|
|
|
|
private val json = """
|
|
{"schedule":{"enabled":true,"cutsMobile":false,
|
|
"windows":[{"days":[1,2,3,4,5],"start":480,"end":1200},{"days":[5],"start":1320,"end":120}]},
|
|
"dailyLimitMin":90,
|
|
"appLimits":[{"pkg":"com.game","min":30}],
|
|
"blocked":["com.bad"],
|
|
"bonus":{"date":"2026-10-26","globalMin":15,"apps":[{"pkg":"com.game","min":10}]}}
|
|
""".trimIndent()
|
|
|
|
@Test fun `lecture complete`() {
|
|
val r = Rules.parse(json)
|
|
assertTrue(r.scheduleEnabled)
|
|
assertFalse(r.scheduleCutsMobile)
|
|
assertEquals(2, r.windows.size)
|
|
assertEquals(90, r.dailyLimitMin)
|
|
assertEquals(listOf(AppLimit("com.game", 30)), r.appLimits)
|
|
assertEquals(setOf("com.bad"), r.blocked)
|
|
assertEquals("2026-10-26", r.bonusDate)
|
|
assertEquals(15, r.bonusGlobalMin)
|
|
assertFalse(r.isEmpty)
|
|
}
|
|
|
|
@Test fun `json vide ou invalide donne aucune regle`() {
|
|
assertTrue(Rules.parse(null).isEmpty)
|
|
assertTrue(Rules.parse("").isEmpty)
|
|
assertTrue(Rules.parse("{pas du json").isEmpty)
|
|
}
|
|
|
|
@Test fun `plage de journee`() {
|
|
val r = Rules.parse(json)
|
|
assertTrue(r.isOpenAt(at(1, 8))) // lundi 08:00 : début inclus
|
|
assertTrue(r.isOpenAt(at(1, 19, 59)))
|
|
assertFalse(r.isOpenAt(at(1, 20))) // fin exclue
|
|
assertFalse(r.isOpenAt(at(1, 7, 59)))
|
|
assertFalse(r.isOpenAt(at(6, 12))) // samedi : pas de plage de journée
|
|
}
|
|
|
|
@Test fun `plage qui passe minuit deborde sur le lendemain`() {
|
|
val r = Rules.parse(json)
|
|
assertTrue(r.isOpenAt(at(5, 23))) // vendredi 23:00
|
|
assertTrue(r.isOpenAt(at(6, 1, 30))) // samedi 01:30, suite de la plage du vendredi
|
|
assertFalse(r.isOpenAt(at(6, 2)))
|
|
assertFalse(r.isOpenAt(at(7, 1))) // dimanche 01:00 : pas de plage samedi soir
|
|
}
|
|
|
|
@Test fun `prochaine ouverture`() {
|
|
val r = Rules.parse(json)
|
|
assertEquals("Reprise à 08:00.", r.nextOpeningText(at(1, 6)))
|
|
assertEquals("Reprise à 08:00 (dans 2 j).", r.nextOpeningText(at(6, 12)))
|
|
}
|
|
}
|