Skip to content

Commit

Permalink
ProjectsRepository Tests
Browse files Browse the repository at this point in the history
Created Datasource for the filesystem access. Makes things more testable.
  • Loading branch information
Wavesonics committed Jun 30, 2024
1 parent 7091c4a commit 48fc4cf
Show file tree
Hide file tree
Showing 10 changed files with 331 additions and 182 deletions.
13 changes: 13 additions & 0 deletions server/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

val app_version: String by extra

plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.powerassert)
alias(libs.plugins.ktor)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.sqldelight)
Expand Down Expand Up @@ -83,3 +85,14 @@ dependencies {
testImplementation(libs.okio.fakefilesystem)
testImplementation(libs.ktor.server.testshostjvm)
}

@OptIn(ExperimentalKotlinGradlePluginApi::class)
powerAssert {
functions = listOf(
"kotlin.assert",
"kotlin.test.assertTrue",
"kotlin.test.assertEquals",
"kotlin.test.assertNull"
)
includedSourceSets = listOf("test")
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,30 @@ import com.darkrockstudios.apps.hammer.account.AccountsRepository
import com.darkrockstudios.apps.hammer.admin.AdminComponent
import com.darkrockstudios.apps.hammer.admin.WhiteListRepository
import com.darkrockstudios.apps.hammer.base.http.createJsonSerializer
import com.darkrockstudios.apps.hammer.database.*
import com.darkrockstudios.apps.hammer.database.AccountDao
import com.darkrockstudios.apps.hammer.database.AuthTokenDao
import com.darkrockstudios.apps.hammer.database.Database
import com.darkrockstudios.apps.hammer.database.SqliteDatabase
import com.darkrockstudios.apps.hammer.database.WhiteListDao
import com.darkrockstudios.apps.hammer.project.ProjectRepository
import com.darkrockstudios.apps.hammer.project.ProjectSyncKey
import com.darkrockstudios.apps.hammer.project.ProjectSynchronizationSession
import com.darkrockstudios.apps.hammer.project.synchronizers.*
import com.darkrockstudios.apps.hammer.project.synchronizers.ServerEncyclopediaSynchronizer
import com.darkrockstudios.apps.hammer.project.synchronizers.ServerNoteSynchronizer
import com.darkrockstudios.apps.hammer.project.synchronizers.ServerSceneDraftSynchronizer
import com.darkrockstudios.apps.hammer.project.synchronizers.ServerSceneSynchronizer
import com.darkrockstudios.apps.hammer.project.synchronizers.ServerTimelineSynchronizer
import com.darkrockstudios.apps.hammer.projects.ProjectsDatasource
import com.darkrockstudios.apps.hammer.projects.ProjectsFileSystemDatasource
import com.darkrockstudios.apps.hammer.projects.ProjectsRepository
import com.darkrockstudios.apps.hammer.projects.ProjectsSynchronizationSession
import com.darkrockstudios.apps.hammer.syncsessionmanager.SyncSessionManager
import io.ktor.util.logging.*
import io.ktor.util.logging.Logger
import kotlinx.coroutines.Dispatchers
import kotlinx.datetime.Clock
import kotlinx.serialization.json.Json
import okio.FileSystem
import org.koin.core.module.dsl.factoryOf
import org.koin.core.module.dsl.singleOf
import org.koin.core.qualifier.named
import org.koin.dsl.bind
Expand Down Expand Up @@ -49,6 +60,8 @@ fun mainModule(logger: Logger) = module {
singleOf(::ProjectRepository)
singleOf(::WhiteListRepository)

factoryOf(::ProjectsFileSystemDatasource) bind ProjectsDatasource::class

singleOf(::AdminComponent)
singleOf(::AccountsComponent)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ import com.darkrockstudios.apps.hammer.base.http.ClientEntityState
import com.darkrockstudios.apps.hammer.base.http.ProjectSynchronizationBegan
import com.darkrockstudios.apps.hammer.dependencyinjection.PROJECTS_SYNC_MANAGER
import com.darkrockstudios.apps.hammer.dependencyinjection.PROJECT_SYNC_MANAGER
import com.darkrockstudios.apps.hammer.project.synchronizers.*
import com.darkrockstudios.apps.hammer.projects.ProjectsRepository.Companion.getUserDirectory
import com.darkrockstudios.apps.hammer.project.synchronizers.ServerEncyclopediaSynchronizer
import com.darkrockstudios.apps.hammer.project.synchronizers.ServerEntitySynchronizer
import com.darkrockstudios.apps.hammer.project.synchronizers.ServerNoteSynchronizer
import com.darkrockstudios.apps.hammer.project.synchronizers.ServerSceneDraftSynchronizer
import com.darkrockstudios.apps.hammer.project.synchronizers.ServerSceneSynchronizer
import com.darkrockstudios.apps.hammer.project.synchronizers.ServerTimelineSynchronizer
import com.darkrockstudios.apps.hammer.projects.ProjectsFileSystemDatasource.Companion.getUserDirectory
import com.darkrockstudios.apps.hammer.projects.ProjectsSynchronizationSession
import com.darkrockstudios.apps.hammer.syncsessionmanager.SyncSessionManager
import com.darkrockstudios.apps.hammer.utilities.Msg
Expand Down Expand Up @@ -43,7 +48,6 @@ class ProjectRepository(
qualifier = named(PROJECT_SYNC_MANAGER)
)

fun getUserDirectory(userId: Long): Path = getUserDirectory(userId, fileSystem)
fun getEntityDirectory(userId: Long, projectDef: ProjectDefinition): Path =
getEntityDirectory(userId, projectDef, fileSystem)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.darkrockstudios.apps.hammer.projects

import com.darkrockstudios.apps.hammer.project.ProjectDefinition
import com.darkrockstudios.apps.hammer.project.ProjectsSyncData

interface ProjectsDatasource {
fun saveSyncData(userId: Long, data: ProjectsSyncData)
fun getProjects(userId: Long): Set<ProjectDefinition>
fun loadSyncData(userId: Long): ProjectsSyncData
fun createUserData(userId: Long)
fun updateSyncData(
userId: Long,
action: (ProjectsSyncData) -> ProjectsSyncData
): ProjectsSyncData

fun deleteProject(userId: Long, projectName: String): Result<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package com.darkrockstudios.apps.hammer.projects

import com.darkrockstudios.apps.hammer.project.ProjectDefinition
import com.darkrockstudios.apps.hammer.project.ProjectRepository
import com.darkrockstudios.apps.hammer.project.ProjectsSyncData
import com.darkrockstudios.apps.hammer.projects.ProjectsRepository.Companion.defaultData
import com.darkrockstudios.apps.hammer.utilities.getRootDataDirectory
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okio.FileSystem
import okio.Path

class ProjectsFileSystemDatasource(
private val fileSystem: FileSystem,
private val json: Json,
) : ProjectsDatasource {

private fun getUserDirectory(userId: Long): Path {
return getUserDirectory(userId, fileSystem)
}

private fun getSyncDataPath(userId: Long): Path = getUserDirectory(userId) / DATA_FILE

override fun createUserData(userId: Long) {
val userDir = getUserDirectory(userId)

fileSystem.createDirectories(userDir)

val dataFile = userDir / DATA_FILE
val data = defaultData(userId)
val dataJson = json.encodeToString(data)
fileSystem.write(dataFile) {
writeUtf8(dataJson)
}

/*
val projectsFile = userDir / PROJECT_FILE
// Create the zip file with a place holder entry
//val zipFs = fileSystem.openZip(projectsFile)
val zipFile = File(projectsFile.toString())
val out = ZipOutputStream(FileOutputStream(zipFile))
val e = ZipEntry(".")
out.putNextEntry(e)
val placeHolderData = "\n".toByteArray()
out.write(placeHolderData, 0, placeHolderData.size)
out.closeEntry()
out.close()
*/
}

override fun deleteProject(userId: Long, projectName: String): Result<Unit> {
val projectDef = ProjectDefinition(projectName)
val projectDir = ProjectRepository.getProjectDirectory(userId, projectDef, fileSystem)
fileSystem.deleteRecursively(projectDir)

updateSyncData(userId) { data ->
data.copy(
deletedProjects = data.deletedProjects + projectName
)
}

return Result.success(Unit)
}

override fun updateSyncData(
userId: Long,
action: (ProjectsSyncData) -> ProjectsSyncData
): ProjectsSyncData {
val data = loadSyncData(userId)
val update = action(data)
saveSyncData(userId, update)
return update
}

override fun saveSyncData(userId: Long, data: ProjectsSyncData) {
val path = getSyncDataPath(userId)
fileSystem.write(path) {
val syncDataJson = json.encodeToString(data)
writeUtf8(syncDataJson)
}
}

override fun getProjects(userId: Long): Set<ProjectDefinition> {
val projectsDir = getUserDirectory(userId)
return fileSystem.list(projectsDir)
.filter { fileSystem.metadata(it).isDirectory }
.filter { it.name.startsWith('.').not() }
.map { path -> ProjectDefinition(path.name) }
.toSet()
}

override fun loadSyncData(userId: Long): ProjectsSyncData {
val path = getSyncDataPath(userId)
return if (fileSystem.exists(path)) {
fileSystem.read(path) {
val syncDataJson = readUtf8()
json.decodeFromString(syncDataJson)
}
} else {
val data = defaultData(userId)
saveSyncData(userId, data)
data
}
}

companion object {
private const val DATA_DIRECTORY = "user_data"
private const val DATA_FILE = "syncData.json"

fun getRootDirectory(fileSystem: FileSystem): Path =
getRootDataDirectory(fileSystem) / DATA_DIRECTORY

fun getUserDirectory(userId: Long, fileSystem: FileSystem): Path {
val dir = getRootDirectory(fileSystem)
return dir / userId.toString()
}

fun getSyncDataPath(userId: Long, fileSystem: FileSystem): Path =
getUserDirectory(userId, fileSystem) / DATA_FILE
}
}
Loading

0 comments on commit 48fc4cf

Please sign in to comment.