// Auteur : Simon-Pierre Boucher — contact@spboucher.ai // AdapterTests.kt — tests des parcours critiques hors réseau : mapping JSON → // KAItem pour chaque adaptateur d'univers (mêmes échantillons réels que la // suite iOS KATests.swift), construction des URL de liste, favoris/collections. package com.groupeka.ka import com.groupeka.ka.core.Ecosystem import com.groupeka.ka.core.FavoritesStore import com.groupeka.ka.core.KAItem import com.groupeka.ka.core.UniverseService import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Test import java.io.File class AdapterTests { private fun decode(json: String): JsonObject = Json.parseToJsonElement(json) as JsonObject @Test fun louKaMapping() { val u = Ecosystem.universe("lou-ka")!! val o = decode("""{"uid":"a:1","title":"4½ Limoilou","city":"Québec","unit_type":"4½","price":1200,"url":"https://x.com/a","lat":46.8,"lng":-71.2,"address":"12 rue X"}""") val item = u.map!!(o)!! assertEquals("lou-ka", item.universeID) assertEquals("4½ Limoilou", item.title) assertTrue(item.priceLabel!!.contains("1")) assertEquals("Québec", item.city) assertNotNull(item.latitude) assertTrue(item.facts.any { it.label == "Taille" && it.value == "4½" }) } @Test fun autoKaMapping() { val u = Ecosystem.universe("auto-ka")!! val o = decode("""{"uid":"v:1","title":"Kia Forte 2010","make":"Kia","model":"Forte","year":2010,"price":1595,"price_label":"1 595 ${'$'}","mileage_label":"260 789 km","url":"https://x.com/v"}""") val item = u.map!!(o)!! assertEquals("1 595 $", item.priceLabel) assertTrue(item.subtitle!!.contains("2010")) assertTrue(item.facts.any { it.label == "Marque" && it.value == "Kia" }) } @Test fun jobKaSalary() { val u = Ecosystem.universe("job-ka")!! val o = decode("""{"uid":"j:1","title":"Analyste","employer":"TD","city":"Montréal","salary_year_min":65000}""") val item = u.map!!(o)!! assertTrue(item.priceLabel!!.contains("/an")) assertTrue(item.subtitle!!.contains("TD")) } @Test fun trouveKaStripsHTML() { val u = Ecosystem.universe("trouve-ka")!! val o = decode("""{"url":"https://y.qc","title":"La poutine","snippet":"un plat & une légende","domain":"y.qc"}""") val item = u.map!!(o)!! assertEquals("La poutine", item.title) assertEquals("un plat & une légende", item.subtitle) } @Test fun foodKaIgnoresPlaceholderPrice() { val u = Ecosystem.universe("food-ka")!! val o = decode("""{"uid":"p:1","name":"Prunes","price":0.01,"brand":"PA"}""") val item = u.map!!(o)!! assertNull("un prix placeholder (0,01 $) ne doit pas s'afficher", item.priceLabel) } @Test fun foodKaShowsSalePrice() { val u = Ecosystem.universe("food-ka")!! val o = decode("""{"uid":"p:2","name":"Beurre","price":4.99,"regular_price":6.49,"on_sale":true,"brand":"Natrel"}""") val item = u.map!!(o)!! assertTrue("prix barré attendu", item.priceLabel!!.contains("rég.")) assertTrue(item.facts.any { it.label == "Solde" }) } @Test fun creaKaPlatformsAndLinks() { val u = Ecosystem.universe("crea-ka")!! val o = decode("""{"cid":"c:1","display_name":"Créateur QC","niches":["humour"],"platforms":[{"platform":"youtube","url":"https://youtube.com/@qc","followers":120000,"handle":"qc"},{"platform":"tiktok","url":"https://tiktok.com/@qc","followers":50000}]}""") val item = u.map!!(o)!! assertTrue("abonnés cumulés attendus dans le sous-titre", item.subtitle!!.contains("abonnés")) assertEquals("https://youtube.com/@qc", item.url) assertTrue(item.links.any { it.label == "Youtube" && it.value == "https://youtube.com/@qc" }) assertTrue(item.facts.any { it.label == "Youtube" }) } @Test fun firstUsableImageFromImagesArray() { val u = Ecosystem.universe("lou-ka")!! val o = decode("""{"uid":"a:2","title":"3½","images":["pas-une-url","https://cdn.x.com/1.jpg","https://cdn.x.com/2.jpg"]}""") val item = u.map!!(o)!! assertEquals("https://cdn.x.com/1.jpg", item.imageURL) } @Test fun everyUniverseHasDistinctAccent() { val accents = Ecosystem.all.map { it.accentHex } assertEquals("chaque univers doit avoir un accent unique", accents.size, accents.toSet().size) // 12 univers — Groupe-KA (portail mère) est l'app elle-même, pas un univers listé assertEquals(12, Ecosystem.all.size) } @Test fun listURLBuilding() { val sorti = Ecosystem.universe("sorti-ka")!! val url = UniverseService.listURL(sorti, query = "jazz", limit = 10)!! assertTrue("la query embarquée du listPath doit survivre", url.contains("upcoming=true")) assertTrue(url.contains("q=jazz")) assertTrue(url.contains("limit=10")) assertTrue(url.startsWith("https://www.sorti-ka.com/api/events")) } @Test fun favoritesToggleAndCollections() { val file = File.createTempFile("ka-favoris", ".json").also { it.delete() } val store = FavoritesStore(file) val item = KAItem(id = "t:1", universeID = "lou-ka", title = "Test") assertFalse(store.isFavorite(item)) store.toggle(item) assertTrue(store.isFavorite(item)) store.addCollection("Week-end") val dest = store.collections.value.last() store.move(item, dest.id) assertTrue(store.collections.value.last().items.any { it.id == item.id }) store.toggle(item) assertFalse(store.isFavorite(item)) file.delete() } @Test fun favoritesPersistAcrossInstances() { val file = File.createTempFile("ka-favoris", ".json").also { it.delete() } val store = FavoritesStore(file) store.toggle(KAItem(id = "t:2", universeID = "auto-ka", title = "Corolla")) val reloaded = FavoritesStore(file) assertTrue(reloaded.isFavorite(KAItem(id = "t:2", universeID = "auto-ka", title = "Corolla"))) file.delete() } }