diff --git a/app/src/test/java/com/example/resbuilder/JobListModelsTest.kt b/app/src/test/java/com/example/resbuilder/JobListModelsTest.kt new file mode 100644 index 0000000..4b9c26f --- /dev/null +++ b/app/src/test/java/com/example/resbuilder/JobListModelsTest.kt @@ -0,0 +1,81 @@ +package com.example.resbuilder + +import com.example.resbuilder.data.model.JobListResponse +import com.example.resbuilder.data.model.JobSummary +import com.google.gson.Gson +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test + +/** + * Tests for the API response models introduced with the "My Resumes" feature. + * + * These verify the exact wire format expected from `GET /jobs` so that a backend + * contract change fails loudly here rather than silently breaking the UI. + */ +class JobListModelsTest { + + private val gson = Gson() + + @Test + fun `parses a completed resume job from the backend shape`() { + val json = """ + { + "jobs": [ + { + "job_id": "abc-123", + "type": "resume", + "status": "completed", + "title": "Senior Android Engineer", + "created_at": "2026-08-04T12:00:00Z" + } + ] + } + """.trimIndent() + + val parsed = gson.fromJson(json, JobListResponse::class.java) + + assertEquals(1, parsed.jobs.size) + val job = parsed.jobs[0] + assertEquals("abc-123", job.job_id) + assertEquals("resume", job.type) + assertEquals("completed", job.status) + assertEquals("Senior Android Engineer", job.title) + assertEquals("2026-08-04T12:00:00Z", job.created_at) + } + + @Test + fun `title is null when backend omits it`() { + val json = """ + {"jobs": [{"job_id": "x", "type": "cover_letter", "status": "queued"}]} + """.trimIndent() + + val parsed = gson.fromJson(json, JobListResponse::class.java) + val job = parsed.jobs[0] + + assertNull(job.title) + assertNull(job.created_at) + assertEquals("cover_letter", job.type) + } + + @Test + fun `empty jobs list parses to empty collection`() { + val json = """{"jobs": []}""" + + val parsed = gson.fromJson(json, JobListResponse::class.java) + + assertTrue(parsed.jobs.isEmpty()) + } + + @Test + fun `JobSummary fields are immutable`() { + val job = JobSummary(job_id = "id", type = "resume", status = "completed") + + // Reflectively assert all fields are val (final). This guards against + // regressions that would make the model mutable and thread-unsafe. + for (field in JobSummary::class.java.declaredFields) { + assertEquals("Field ${field.name} must be final", java.lang.reflect.Modifier.FINAL, field.modifiers and java.lang.reflect.Modifier.FINAL) + } + } +}