Загрузка данных


package ru.notesapp.util

import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import ru.notesapp.domain.SimpleNote
import ru.notesapp.domain.User

@MiniSerializable
private data class OneField(val value: String)

@MiniSerializable
private data class ThreeFields(val a: String, val b: Long, val c: Boolean)

@MiniSerializable(name = "renamed")
private data class RenamedClass(val x: String)

@MiniSerializable
private data class MixedFields(
    @MiniSerializable(name = "renamed_a") val a: String,
    val b: String,
)

@MiniSerializable
private data class EmptyName(
    @MiniSerializable(name = "") val a: String,
)

@MiniSerializable
private data class IntField(val a: Int)
@MiniSerializable
private data class LongField(val a: Long)
@MiniSerializable
private data class DoubleField(val a: Double)
@MiniSerializable
private data class FloatField(val a: Float)
@MiniSerializable
private data class BooleanField(val a: Boolean)
@MiniSerializable
private data class NullableField(val a: String?)

@MiniSerializable
private data class OptionalDefault(val a: String = "default")

private data class NoAnnotation(val a: String)

class MiniJsonTest {

    // ---------- toJson: базовые типы (8) ----------

    @Test fun toJson_string_field_wrapped_in_quotes() {
        val json = OneField("hi").toJson()
        assertTrue(json.contains("\"value\":\"hi\""))
    }

    @Test fun toJson_int_field_no_quotes() {
        val json = IntField(42).toJson()
        assertTrue(json.contains("\"a\":42"))
        assertFalse(json.contains("\"42\""))
    }

    @Test fun toJson_long_field_no_quotes() {
        val json = LongField(999L).toJson()
        assertTrue(json.contains("\"a\":999"))
    }

    @Test fun toJson_double_field_no_quotes() {
        val json = DoubleField(3.14).toJson()
        assertTrue(json.contains("\"a\":3.14"))
    }

    @Test fun toJson_float_field_no_quotes() {
        val json = FloatField(2.5f).toJson()
        assertTrue(json.contains("\"a\":2.5"))
    }

    @Test fun toJson_boolean_true_field_lowercase() {
        val json = BooleanField(true).toJson()
        assertTrue(json.contains("\"a\":true"))
    }

    @Test fun toJson_boolean_false_field_lowercase() {
        val json = BooleanField(false).toJson()
        assertTrue(json.contains("\"a\":false"))
    }

    @Test fun toJson_null_field_emits_null_without_quotes() {
        val json = NullableField(null).toJson()
        assertTrue(json.contains("\"a\":null"))
        assertFalse(json.contains("\"null\""))
    }

    // ---------- toJson: структуры (6) ----------

    @Test fun toJson_single_field_object() {
        val json = OneField("x").toJson()
        assertEquals("{\"value\":\"x\"}", json)
    }

    @Test fun toJson_three_fields_object() {
        val json = ThreeFields("s", 1L, true).toJson()
        assertTrue(json.startsWith("{"))
        assertTrue(json.endsWith("}"))
        assertTrue(json.contains("\"a\":\"s\""))
        assertTrue(json.contains("\"b\":1"))
        assertTrue(json.contains("\"c\":true"))
    }

    @Test fun toJson_field_separator_is_comma() {
        val json = ThreeFields("s", 1L, true).toJson()
        assertTrue(json.contains(","))
    }

    @Test fun toJson_key_value_separator_is_colon() {
        val json = OneField("x").toJson()
        assertTrue(json.contains(":"))
    }

    @Test fun toJson_object_starts_with_brace() {
        assertTrue(OneField("x").toJson().startsWith("{"))
    }

    @Test fun toJson_object_ends_with_brace() {
        assertTrue(OneField("x").toJson().endsWith("}"))
    }

    // ---------- toJson: аннотации (5) ----------

    @Test fun toJson_uses_annotated_name_when_present() {
        val user = User(1L, "Alice", null, 30)
        val json = user.toJson()
        assertTrue(json.contains("\"user_id\":1"))
        assertTrue(json.contains("\"user_name\":\"Alice\""))
    }

    @Test fun toJson_uses_property_name_when_annotation_absent() {
        val user = User(1L, "Alice", null, 30)
        val json = user.toJson()
        assertTrue(json.contains("\"email\""))
        assertTrue(json.contains("\"age\""))
    }

    @Test fun toJson_uses_class_annotation_for_root() {
        val json = RenamedClass("x").toJson()
        assertTrue(json.startsWith("{"))
    }

    @Test fun toJson_annotation_empty_string_falls_back_to_property_name() {
        val json = EmptyName("x").toJson()
        assertTrue(json.contains("\"a\":\"x\""))
    }

    @Test fun toJson_mixed_annotated_and_non_annotated_fields() {
        val json = MixedFields("x", "y").toJson()
        assertTrue(json.contains("\"renamed_a\":\"x\""))
        assertTrue(json.contains("\"b\":\"y\""))
    }

    // ---------- toJson: escape (5) ----------

    @Test fun toJson_string_with_quote_escaped() {
        val json = OneField("say \"hi\"").toJson()
        assertTrue(json.contains("\\\""))
    }

    @Test fun toJson_string_with_backslash_escaped() {
        val json = OneField("a\\b").toJson()
        assertTrue(json.contains("\\\\"))
    }

    @Test fun toJson_string_with_newline_escaped() {
        val json = OneField("a\nb").toJson()
        assertTrue(json.contains("\\n"))
    }

    @Test fun toJson_string_with_tab_escaped() {
        val json = OneField("a\tb").toJson()
        assertTrue(json.contains("\\t"))
    }

    @Test fun toJson_string_with_unicode_preserved() {
        val json = OneField("Привет").toJson()
        assertTrue(json.contains("Привет"))
    }

    // ---------- toJson: ошибки (3) ----------

    @Test(expected = IllegalArgumentException::class)
    fun toJson_class_without_annotation_throws_IllegalArgumentException() {
        NoAnnotation("x").toJson()
    }

    @Test fun toJson_class_without_annotation_error_message_includes_class_name() {
        try {
            NoAnnotation("x").toJson()
            assertTrue(false)
        } catch (e: IllegalArgumentException) {
            assertTrue(e.message?.contains("NoAnnotation") == true)
        }
    }

    @Test fun toJson_non_data_class_with_annotation_throws_or_succeeds() {
        // Наш сериализатор работает через declaredMemberProperties,
        // поэтому обычный класс тоже сериализуется, если помечен аннотацией.
        val json = OneField("ok").toJson()
        assertTrue(json.contains("ok"))
    }

    // ---------- fromJson: базовые типы (5) ----------

    @Test fun fromJson_string_field_parsed_correctly() {
        val obj = fromJson("""{"value":"hello"}""", OneField::class)
        assertEquals("hello", obj.value)
    }

    @Test fun fromJson_int_field_parsed_correctly() {
        val obj = fromJson("""{"a":42}""", IntField::class)
        assertEquals(42, obj.a)
    }

    @Test fun fromJson_long_field_parsed_correctly() {
        val obj = fromJson("""{"a":999}""", LongField::class)
        assertEquals(999L, obj.a)
    }

    @Test fun fromJson_boolean_field_parsed_correctly() {
        val obj = fromJson("""{"a":true}""", BooleanField::class)
        assertEquals(true, obj.a)
    }

    @Test fun fromJson_null_field_becomes_null_property() {
        val obj = fromJson("""{"a":null}""", NullableField::class)
        assertEquals(null, obj.a)
    }

    // ---------- fromJson: структуры (4) ----------

    @Test fun fromJson_single_field_object() {
        val obj = fromJson("""{"value":"x"}""", OneField::class)
        assertEquals("x", obj.value)
    }

    @Test fun fromJson_multiple_fields_object() {
        val obj = fromJson("""{"a":"s","b":1,"c":true}""", ThreeFields::class)
        assertEquals("s", obj.a)
        assertEquals(1L, obj.b)
        assertEquals(true, obj.c)
    }

    @Test fun fromJson_uses_annotated_name_to_map_to_property() {
        val obj = fromJson("""{"renamed_a":"x","b":"y"}""", MixedFields::class)
        assertEquals("x", obj.a)
        assertEquals("y", obj.b)
    }

    @Test fun fromJson_missing_optional_field_uses_default() {
        val obj = fromJson("""{}""", OptionalDefault::class)
        assertEquals("default", obj.a)
    }

    // ---------- Round-trip (4) ----------

    @Test fun round_trip_user_with_email_preserves_all_fields() {
        val u = User(1L, "Alice", "a@b.c", 30)
        assertEquals(u, fromJson(u.toJson(), User::class))
    }

    @Test fun round_trip_user_with_null_email_preserves_null() {
        val u = User(2L, "Bob", null, 20)
        assertEquals(u, fromJson(u.toJson(), User::class))
    }

    @Test fun round_trip_simple_note_preserves_all_fields() {
        val n = SimpleNote(1L, "T", "C", 100L, "https://x.y/z")
        assertEquals(n, fromJson(n.toJson(), SimpleNote::class))
    }

    @Test fun round_trip_unicode_strings_preserved() {
        val u = User(3L, "Иван", "иван@почта.рф", 25)
        assertEquals(u, fromJson(u.toJson(), User::class))
    }
}