Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ PRIMARY KEY (event_id, session_id, category_id)
upsertSessionCategory:
INSERT OR REPLACE INTO SessionCategory VALUES ?;

deleteSessionCategoriesByTalks:
DELETE FROM SessionCategory WHERE event_id == ? AND session_id IN ?;

selectCategories:
SELECT id, name, color, icon, selected
FROM Category
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,9 @@ WHERE EventSession.event_id = ? AND EventSession.id = ?;

upsertEventSession:
INSERT OR REPLACE INTO EventSession VALUES ?;

diffEventSessions:
SELECT id FROM EventSession WHERE event_id == ? AND id NOT IN ?;

deleteEventSessions:
DELETE FROM EventSession WHERE event_id == ? AND id IN ?;
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ PRIMARY KEY (event_id, session_id, format_id)
upsertSessionFormat:
INSERT OR REPLACE INTO SessionFormat VALUES ?;

deleteSessionFormatsByTalks:
DELETE FROM SessionFormat WHERE event_id == ? AND session_id IN ?;

selectFormats:
SELECT id, name, time, selected
FROM Format
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ FROM Shape
WHERE event_id == ?
ORDER BY order_;

deleteShapes:
DELETE FROM Shape WHERE event_id == ? AND map_id IN ?;
deleteShapesByEvent:
DELETE FROM Shape WHERE event_id == ?;

insertPictogram:
INSERT OR REPLACE INTO Pictogram(map_id, event_id, order_, name, description, x, y, type)
Expand All @@ -85,5 +85,5 @@ FROM Pictogram
WHERE event_id == ?
ORDER BY order_;

deletePictograms:
DELETE FROM Pictogram WHERE event_id == ? AND map_id IN ?;
deletePictogramsByEvent:
DELETE FROM Pictogram WHERE event_id == ?;
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ FOREIGN KEY (event_id) REFERENCES Event(id)
upsertSchedule:
INSERT OR REPLACE INTO Schedule(id, order_, session_id, session_type, start_time, end_time, room, event_id)
VALUES ?;

diffSchedules:
SELECT id FROM Schedule WHERE event_id == ? AND id NOT IN ?;

deleteSchedules:
DELETE FROM Schedule WHERE event_id == ? AND id IN ?;
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,13 @@ WHERE TalkSessionWithSpeakers.event_id = ? AND TalkSessionWithSpeakers.speaker_i
markAsFavorite:
UPDATE TalkSession SET is_favorite = ? WHERE id == ? AND event_id == ?;

upsertTalkSession:
INSERT OR REPLACE INTO TalkSession VALUES ?;
upsertTalkSession {
INSERT OR IGNORE INTO TalkSession(id, event_id, title, abstract, level, language, slide_url, replay_url, open_feedback_url, is_favorite)
VALUES (:id, :event_id, :title, :abstract_, :level, :language, :slide_url, :replay_url, :open_feedback_url, 0);
UPDATE TalkSession SET title = :title, abstract = :abstract_, level = :level, language = :language,
slide_url = :slide_url, replay_url = :replay_url, open_feedback_url = :open_feedback_url, event_id = :event_id
WHERE id = :id;
}

upsertTalkWithSpeakersSession:
INSERT OR REPLACE INTO TalkSessionWithSpeakers(speaker_id, talk_id, event_id) VALUES ?;
Expand All @@ -110,11 +115,3 @@ WHERE event_id == ? AND talk_id NOT IN ?;

deleteTalkWithSpeakers:
DELETE FROM TalkSessionWithSpeakers WHERE event_id == ? AND talk_id IN ?;

diffSessions:
SELECT id
FROM TalkSession
WHERE event_id == ? AND id NOT IN ?;

deleteSessions:
DELETE FROM TalkSession WHERE event_id == ? AND id IN ?;
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ insertSocial:
INSERT OR REPLACE INTO Social(url, type, ext_id, event_id)
VALUES (?, ?, ?, ?);

deleteSocialsByExtIds:
DELETE FROM Social WHERE event_id == ? AND ext_id IN ?;

selectSocials:
SELECT Social.url, Social.type
FROM Social
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ PRIMARY KEY (event_id, session_id, tag_id)
upsertSessionTag:
INSERT OR REPLACE INTO SessionTag VALUES ?;

deleteSessionTagsByTalks:
DELETE FROM SessionTag WHERE event_id == ? AND session_id IN ?;

selectTagsBySessionId:
SELECT Tag.id, Tag.name
FROM Tag
Expand Down
5 changes: 5 additions & 0 deletions shared/core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ kotlin {
dependsOn(commonMain)
dependsOn(mobileMain)
}
val desktopTest by getting {
dependencies {
implementation(libs.cash.sqldelight.desktop)
}
}
}

sourceSets.all {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ internal class EventRepositoryImpl(
sessionDao.insertAgenda(eventId, exportPlanning)
partnerDao.insertPartners(eventId, exportPartners)
} catch (ex: Throwable) {
println("fetchAndStoreAgenda failed for event '$eventId': ${ex.message}")
ex.printStackTrace()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.paligot.confily.core

import com.paligot.confily.core.test.inMemoryDatabase
import kotlin.test.Test
import kotlin.test.assertTrue

class DatabaseHarnessTest {
@Test
fun in_memory_database_enforces_foreign_keys() {
val db = inMemoryDatabase()
// Shape.map_id references Map(id); inserting with a missing map must fail.
val threw = try {
db.mapQueries.insertShape(
order_ = 0,
name = "x",
description = null,
type = "Room",
start_x = 0.0,
start_y = 0.0,
end_x = 1.0,
end_y = 1.0,
map_id = "missing-map",
event_id = "missing-event"
)
false
} catch (e: Throwable) {
true
}
assertTrue(threw, "Expected a foreign-key constraint violation")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.paligot.confily.core.maps

import com.paligot.confily.core.test.Fixtures
import com.paligot.confily.core.test.inMemoryDatabase
import com.paligot.confily.core.test.seedEvent
import com.paligot.confily.db.ConfilyDatabase
import kotlinx.coroutines.Dispatchers
import kotlin.test.Test
import kotlin.test.assertEquals

class MapSyncTest {
private val eventId = "event-1"

private fun dao(db: ConfilyDatabase) =
MapDaoSQLDelight(db, Dispatchers.Unconfined)

@Test
fun shapes_and_pictograms_do_not_accumulate_across_syncs() {
val db = inMemoryDatabase()
db.seedEvent(eventId)
val dao = dao(db)
val maps = listOf(
Fixtures.map(
id = "m1",
shapes = listOf(Fixtures.shape(0), Fixtures.shape(1)),
pictograms = listOf(Fixtures.pictogram(0))
)
)

dao.insertMaps(eventId, maps)
dao.insertMaps(eventId, maps)

assertEquals(2, db.mapQueries.selectShapes(eventId).executeAsList().size)
assertEquals(1, db.mapQueries.selectPictograms(eventId).executeAsList().size)
}

@Test
fun removed_map_and_its_children_are_deleted() {
val db = inMemoryDatabase()
db.seedEvent(eventId)
val dao = dao(db)

dao.insertMaps(
eventId,
listOf(
Fixtures.map("m1", shapes = listOf(Fixtures.shape(0))),
Fixtures.map("m2", shapes = listOf(Fixtures.shape(0)))
)
)
dao.insertMaps(eventId, listOf(Fixtures.map("m1", shapes = listOf(Fixtures.shape(0)))))

assertEquals(1, db.mapQueries.selectMaps(eventId).executeAsList().size)
assertEquals(1, db.mapQueries.selectShapes(eventId).executeAsList().size)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package com.paligot.confily.core.schedules

import com.paligot.confily.core.kvalue.ConferenceSettings
import com.paligot.confily.core.test.Fixtures
import com.paligot.confily.core.test.inMemoryDatabase
import com.paligot.confily.core.test.seedEvent
import com.paligot.confily.db.ConfilyDatabase
import com.russhwolf.settings.ExperimentalSettingsApi
import com.russhwolf.settings.MapSettings
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlin.test.Test
import kotlin.test.assertEquals

@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class, ExperimentalSettingsApi::class)
class SessionSyncTest {
private val eventId = "event-1"

private fun dao(db: ConfilyDatabase) = SessionDaoSQLDelight(
db = db,
settings = ConferenceSettings(MapSettings()).apply { insertEventId(eventId) },
dispatcher = Dispatchers.Unconfined
)

@Test
fun favorite_survives_a_resync() {
val db = inMemoryDatabase()
db.seedEvent(eventId)
val dao = dao(db)
val agenda = Fixtures.agenda(
categories = listOf(Fixtures.category("c1")),
formats = listOf(Fixtures.format("f1")),
sessions = listOf(Fixtures.talk("t1", categoryId = "c1", formatId = "f1")),
schedules = listOf(Fixtures.schedule("s1", sessionId = "t1"))
)

dao.insertAgenda(eventId, agenda)
db.sessionQueries.markAsFavorite(is_favorite = true, id = "t1", event_id = eventId)
dao.insertAgenda(eventId, agenda)

assertEquals(
1L,
db.sessionQueries.countSessionsByFavorite(event_id = eventId, is_favorite = true)
.executeAsOne()
)
}

@Test
fun resync_with_same_payload_does_not_duplicate_sessions() {
val db = inMemoryDatabase()
db.seedEvent(eventId)
val dao = dao(db)
val agenda = Fixtures.agenda(
categories = listOf(Fixtures.category("c1")),
formats = listOf(Fixtures.format("f1")),
sessions = listOf(Fixtures.talk("t1", categoryId = "c1", formatId = "f1")),
schedules = listOf(Fixtures.schedule("s1", sessionId = "t1"))
)

dao.insertAgenda(eventId, agenda)
dao.insertAgenda(eventId, agenda)

assertEquals(1, db.sessionQueries.selectSessions(eventId).executeAsList().size)
}

@Test
fun changing_a_sessions_category_replaces_the_old_link_without_fk_crash() {
val db = inMemoryDatabase()
db.seedEvent(eventId)
val dao = dao(db)
dao.insertAgenda(
eventId,
Fixtures.agenda(
categories = listOf(Fixtures.category("c1")),
formats = listOf(Fixtures.format("f1")),
sessions = listOf(Fixtures.talk("t1", categoryId = "c1", formatId = "f1")),
schedules = listOf(Fixtures.schedule("s1", sessionId = "t1"))
)
)

// c1 is removed from the backend; the talk now uses c2.
dao.insertAgenda(
eventId,
Fixtures.agenda(
categories = listOf(Fixtures.category("c2")),
formats = listOf(Fixtures.format("f1")),
sessions = listOf(Fixtures.talk("t1", categoryId = "c2", formatId = "f1")),
schedules = listOf(Fixtures.schedule("s1", sessionId = "t1"))
)
)

val rows = db.sessionQueries.selectSessions(eventId).executeAsList()
assertEquals(1, rows.size)
assertEquals("c2", rows.single().category_id)
}

@Test
fun removed_session_speaker_tag_and_schedule_are_deleted() {
val db = inMemoryDatabase()
db.seedEvent(eventId)
val dao = dao(db)
dao.insertAgenda(
eventId,
Fixtures.agenda(
categories = listOf(Fixtures.category("c1")),
formats = listOf(Fixtures.format("f1")),
tags = listOf(Fixtures.tag("tag1"), Fixtures.tag("tag2")),
speakers = listOf(Fixtures.speaker("sp1"), Fixtures.speaker("sp2")),
sessions = listOf(
Fixtures.talk("t1", "c1", "f1", tagIds = listOf("tag1"), speakers = listOf("sp1")),
Fixtures.talk("t2", "c1", "f1", tagIds = listOf("tag2"), speakers = listOf("sp2"))
),
schedules = listOf(
Fixtures.schedule("s1", "t1"),
Fixtures.schedule("s2", "t2")
)
)
)

// Second sync keeps only t1 / tag1 / sp1.
dao.insertAgenda(
eventId,
Fixtures.agenda(
categories = listOf(Fixtures.category("c1")),
formats = listOf(Fixtures.format("f1")),
tags = listOf(Fixtures.tag("tag1")),
speakers = listOf(Fixtures.speaker("sp1")),
sessions = listOf(
Fixtures.talk("t1", "c1", "f1", tagIds = listOf("tag1"), speakers = listOf("sp1"))
),
schedules = listOf(Fixtures.schedule("s1", "t1"))
)
)

assertEquals(1, db.sessionQueries.selectSessions(eventId).executeAsList().size)
assertEquals(
emptyList(),
db.tagQueries.selectTags(eventId).executeAsList().map { it.id }.filter { it == "tag2" }
)
assertEquals(0, db.speakerQueries.diffSpeakers(eventId, listOf("sp1", "sp2")).executeAsList().size)
}
}
Loading
Loading