Skip to content

Commit

Permalink
session things
Browse files Browse the repository at this point in the history
  • Loading branch information
alyssaruth committed Oct 16, 2024
1 parent 75edc46 commit a0c1fb4
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 18 deletions.
1 change: 1 addition & 0 deletions buildSrc/src/main/kotlin/DependencyVersions.kt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
object DependencyVersions {
const val KTFMT = "0.15.1"
const val KTOR = "2.3.12"
const val JACKSON = "2.16.1"
const val LOGBACK = "1.5.8"
Expand Down
2 changes: 1 addition & 1 deletion client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent

plugins {
id("Entropy.kotlin-common-conventions")
id("com.ncorti.ktfmt.gradle") version "0.15.1"
id("com.ncorti.ktfmt.gradle") version DependencyVersions.KTFMT
application
}

Expand Down
2 changes: 1 addition & 1 deletion core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent

plugins {
id("Entropy.kotlin-common-conventions")
id("com.ncorti.ktfmt.gradle") version "0.15.1"
id("com.ncorti.ktfmt.gradle") version DependencyVersions.KTFMT
`java-library`
}

Expand Down
1 change: 1 addition & 0 deletions core/src/main/kotlin/http/ClientErrorCodes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ val UPDATE_REQUIRED = ClientErrorCode("updateRequired")
val INVALID_API_VERSION = ClientErrorCode("invalidApiVersion")
val EMPTY_NAME = ClientErrorCode("emptyName")
val INVALID_ACHIEVEMENT_COUNT = ClientErrorCode("invalidAchievementCount")
val INVALID_SESSION = ClientErrorCode("invalidSession")
5 changes: 5 additions & 0 deletions core/src/main/kotlin/http/CustomHeader.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package http

object CustomHeader {
const val SESSION_ID = "session-id"
}
2 changes: 1 addition & 1 deletion server/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent

plugins {
id("Entropy.kotlin-common-conventions")
id("com.ncorti.ktfmt.gradle") version "0.15.1"
id("com.ncorti.ktfmt.gradle") version DependencyVersions.KTFMT
id("io.ktor.plugin") version DependencyVersions.KTOR
application
}
Expand Down
42 changes: 42 additions & 0 deletions server/src/main/kotlin/routes/RoutingUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package routes

import auth.Session
import http.CustomHeader
import http.INVALID_SESSION
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.*
import java.util.*
import util.ServerGlobals

suspend fun requiresSession(call: ApplicationCall, fn: suspend (session: Session) -> Unit) {
val sessionIdStr =
call.request.header(CustomHeader.SESSION_ID)
?: throw ClientException(
HttpStatusCode.Unauthorized,
INVALID_SESSION,
"Missing session id",
)

val sessionId =
try {
UUID.fromString(sessionIdStr)
} catch (e: IllegalArgumentException) {
throw ClientException(
HttpStatusCode.BadRequest,
INVALID_SESSION,
"Session ID was not a valid UUID",
e,
)
}

val session =
ServerGlobals.sessionStore.find(sessionId)
?: throw ClientException(
HttpStatusCode.Unauthorized,
INVALID_SESSION,
"No session found for ID $sessionId",
)

fn(session)
}
71 changes: 58 additions & 13 deletions server/src/test/kotlin/plugins/RoutingTest.kt
Original file line number Diff line number Diff line change
@@ -1,34 +1,37 @@
package plugins

import http.ClientErrorCode
import http.CustomHeader
import io.kotest.matchers.longs.shouldBeGreaterThan
import io.kotest.matchers.nulls.shouldNotBeNull
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.types.shouldBeInstanceOf
import io.ktor.client.request.get
import io.ktor.client.request.header
import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.Application
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
import io.ktor.server.testing.testApplication
import java.util.UUID
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.testing.*
import java.util.*
import logging.KEY_DURATION
import logging.KEY_REQUEST_ID
import logging.KEY_ROUTE
import logging.findLogField
import org.junit.jupiter.api.Test
import routes.ClientException
import routes.requiresSession
import testCore.AbstractTest
import testCore.shouldContainKeyValues
import testCore.shouldMatchJson
import util.ServerGlobals
import util.makeSession

class RoutingTest : AbstractTest() {
@Test
fun `Should handle client errors`() = testApplication {
application { ErrorThrowingController.installRoutes(this) }
application { TestingController.installRoutes(this) }

val response = client.get("/client-error")
response.status shouldBe HttpStatusCode.Conflict
Expand All @@ -42,7 +45,7 @@ class RoutingTest : AbstractTest() {

@Test
fun `Should handle unexpected errors`() = testApplication {
application { ErrorThrowingController.installRoutes(this) }
application { TestingController.installRoutes(this) }

val response = client.get("/internal-error")
response.status shouldBe HttpStatusCode.InternalServerError
Expand Down Expand Up @@ -100,9 +103,50 @@ class RoutingTest : AbstractTest() {
val requestId = receivedLog.findLogField(KEY_REQUEST_ID)
requestId.shouldNotBeNull()
}

@Test
fun `Should reject a call with no session ID`() = testApplication {
application { TestingController.installRoutes(this) }

val response = client.get("/username")
response.status shouldBe HttpStatusCode.Unauthorized
response.bodyAsText().shouldContain("Missing session id")
}

@Test
fun `Should reject a call with a malformed session ID`() = testApplication {
application { TestingController.installRoutes(this) }

val response = client.get("/username") { header(CustomHeader.SESSION_ID, "foo") }
response.status shouldBe HttpStatusCode.BadRequest
response.bodyAsText().shouldContain("Session ID was not a valid UUID")
}

@Test
fun `Should reject a call with a nonexistent session ID`() = testApplication {
application { TestingController.installRoutes(this) }

val sessionId = UUID.randomUUID()

val response = client.get("/username") { header(CustomHeader.SESSION_ID, sessionId) }
response.status shouldBe HttpStatusCode.Unauthorized
response.bodyAsText().shouldContain("No session found for ID $sessionId")
}

@Test
fun `Should successfully extract a session that exists`() = testApplication {
application { TestingController.installRoutes(this) }

val session = makeSession()
ServerGlobals.sessionStore.put(session)

val response = client.get("/username") { header(CustomHeader.SESSION_ID, session.id) }
response.status shouldBe HttpStatusCode.OK
response.bodyAsText().shouldBe(session.name)
}
}

private object ErrorThrowingController {
private object TestingController {
fun installRoutes(application: Application) {
application.routing {
get("/internal-error") { throw NullPointerException("Test error") }
Expand All @@ -113,6 +157,7 @@ private object ErrorThrowingController {
"Entity conflicts with another",
)
}
get("/username") { requiresSession(call) { call.respond(it.name) } }
}
}
}
3 changes: 2 additions & 1 deletion server/src/test/kotlin/util/TestFactory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ fun makeSession(
id: UUID = UUID.randomUUID(),
name: String = "Alyssa",
ip: String = "1.2.3.4",
achievementCount: Int = 4,
apiVersion: Int = OnlineConstants.API_VERSION
) = Session(id, name, ip, apiVersion)
) = Session(id, name, ip, achievementCount, apiVersion)

fun makeGameSettings(
mode: GameMode = GameMode.Entropy,
Expand Down
2 changes: 1 addition & 1 deletion test-core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins {
id("Entropy.kotlin-common-conventions")
id("com.ncorti.ktfmt.gradle") version "0.15.1"
id("com.ncorti.ktfmt.gradle") version DependencyVersions.KTFMT
`java-library`
}

Expand Down

0 comments on commit a0c1fb4

Please sign in to comment.