@@ -0,0 +1,167 @@
1+package com.parseable.android
3+import com.parseable.android.ui.screens.logviewer.LogSeverity
4+import com.parseable.android.ui.screens.logviewer.detectSeverity
5+import kotlinx.serialization.json.JsonObject
6+import kotlinx.serialization.json.JsonPrimitive
7+import org.junit.Assert.assertEquals
10+class SeverityDetectionTest {
12+ private fun logWith(vararg pairs: Pair<String, String>): JsonObject {
13+ return JsonObject(pairs.associate { (k, v) -> k to JsonPrimitive(v) })
16+ // --- Field name detection ---
19+ fun `detects level field`() {
20+ assertEquals(LogSeverity.ERROR, detectSeverity(logWith("level" to "error")))
24+ fun `detects severity field`() {
25+ assertEquals(LogSeverity.WARNING, detectSeverity(logWith("severity" to "warn")))
29+ fun `detects loglevel field`() {
30+ assertEquals(LogSeverity.INFO, detectSeverity(logWith("loglevel" to "info")))
34+ fun `detects log_level field`() {
35+ assertEquals(LogSeverity.DEBUG, detectSeverity(logWith("log_level" to "debug")))
39+ fun `field name matching is case-insensitive`() {
40+ assertEquals(LogSeverity.ERROR, detectSeverity(logWith("Level" to "ERROR")))
41+ assertEquals(LogSeverity.ERROR, detectSeverity(logWith("SEVERITY" to "error")))
42+ assertEquals(LogSeverity.ERROR, detectSeverity(logWith("LogLevel" to "ERR")))
46+ fun `returns UNKNOWN when no severity field present`() {
47+ assertEquals(LogSeverity.UNKNOWN, detectSeverity(logWith("message" to "hello", "host" to "server1")))
51+ fun `returns UNKNOWN for empty log entry`() {
52+ assertEquals(LogSeverity.UNKNOWN, detectSeverity(JsonObject(emptyMap())))
55+ // --- String value matching ---
58+ fun `matches fatal variants`() {
59+ for (value in listOf("fatal", "FATAL", "F", "crit", "critical", "emerg", "emergency", "alert", "panic")) {
60+ assertEquals("Expected FATAL for '$value'", LogSeverity.FATAL, detectSeverity(logWith("level" to value)))
65+ fun `matches error variants`() {
66+ for (value in listOf("error", "ERROR", "err", "E", "severe", "failure", "fail")) {
67+ assertEquals("Expected ERROR for '$value'", LogSeverity.ERROR, detectSeverity(logWith("level" to value)))
72+ fun `matches warning variants`() {
73+ for (value in listOf("warn", "WARNING", "W", "warning")) {
74+ assertEquals("Expected WARNING for '$value'", LogSeverity.WARNING, detectSeverity(logWith("level" to value)))
79+ fun `matches info variants`() {
80+ for (value in listOf("info", "INFO", "I", "information", "informational", "notice", "ok", "success")) {
81+ assertEquals("Expected INFO for '$value'", LogSeverity.INFO, detectSeverity(logWith("level" to value)))
86+ fun `matches debug variants`() {
87+ for (value in listOf("debug", "DEBUG", "D", "fine", "config", "finer")) {
88+ assertEquals("Expected DEBUG for '$value'", LogSeverity.DEBUG, detectSeverity(logWith("level" to value)))
93+ fun `matches trace variants`() {
94+ for (value in listOf("trace", "TRACE", "T", "V", "verbose", "finest", "all")) {
95+ assertEquals("Expected TRACE for '$value'", LogSeverity.TRACE, detectSeverity(logWith("level" to value)))
100+ fun `value matching is case-insensitive`() {
101+ assertEquals(LogSeverity.ERROR, detectSeverity(logWith("level" to "Error")))
102+ assertEquals(LogSeverity.WARNING, detectSeverity(logWith("level" to "WaRnInG")))
106+ fun `trims whitespace from values`() {
107+ assertEquals(LogSeverity.ERROR, detectSeverity(logWith("level" to " error ")))
111+ fun `returns UNKNOWN for unrecognized values`() {
112+ assertEquals(LogSeverity.UNKNOWN, detectSeverity(logWith("level" to "custom_level")))
115+ // --- Numeric values ---
118+ fun `syslog severity numeric values`() {
119+ assertEquals(LogSeverity.FATAL, detectSeverity(logWith("syslog_severity" to "0"))) // emergency
120+ assertEquals(LogSeverity.FATAL, detectSeverity(logWith("syslog_severity" to "1"))) // alert
121+ assertEquals(LogSeverity.FATAL, detectSeverity(logWith("syslog_severity" to "2"))) // critical
122+ assertEquals(LogSeverity.ERROR, detectSeverity(logWith("syslog_severity" to "3"))) // error
123+ assertEquals(LogSeverity.WARNING, detectSeverity(logWith("syslog_severity" to "4"))) // warning
124+ assertEquals(LogSeverity.INFO, detectSeverity(logWith("syslog_severity" to "5"))) // notice
125+ assertEquals(LogSeverity.INFO, detectSeverity(logWith("syslog_severity" to "6"))) // informational
126+ assertEquals(LogSeverity.DEBUG, detectSeverity(logWith("syslog_severity" to "7"))) // debug
130+ fun `HTTP status code mapping`() {
131+ assertEquals(LogSeverity.INFO, detectSeverity(logWith("status" to "200")))
132+ assertEquals(LogSeverity.INFO, detectSeverity(logWith("status" to "301")))
133+ assertEquals(LogSeverity.WARNING, detectSeverity(logWith("status" to "404")))
134+ assertEquals(LogSeverity.ERROR, detectSeverity(logWith("status" to "500")))
135+ assertEquals(LogSeverity.ERROR, detectSeverity(logWith("status" to "503")))
139+ fun `generic numeric level mapping`() {
140+ assertEquals(LogSeverity.FATAL, detectSeverity(logWith("level" to "1000")))
141+ assertEquals(LogSeverity.ERROR, detectSeverity(logWith("level" to "900")))
142+ assertEquals(LogSeverity.WARNING, detectSeverity(logWith("level" to "800")))
143+ assertEquals(LogSeverity.INFO, detectSeverity(logWith("level" to "700")))
144+ assertEquals(LogSeverity.DEBUG, detectSeverity(logWith("level" to "500")))
145+ assertEquals(LogSeverity.TRACE, detectSeverity(logWith("level" to "100")))
148+ // --- Priority among fields ---
151+ fun `level field takes priority over status`() {
152+ val log = JsonObject(mapOf(
153+ "level" to JsonPrimitive("error"),
154+ "status" to JsonPrimitive("200"),
156+ assertEquals(LogSeverity.ERROR, detectSeverity(log))
160+ fun `first matching field with recognized value wins`() {
161+ val log = JsonObject(mapOf(
162+ "level" to JsonPrimitive("unknown_value"),
163+ "severity" to JsonPrimitive("warn"),
165+ assertEquals(LogSeverity.WARNING, detectSeverity(log))