From eeafe2f0f3a3718bd52e9c333894e1abbc0611ab Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Apr 2026 05:35:49 +0000 Subject: feat: vessel registry, crew management, and thermal alarm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Vessel/CrewMember data classes with full profile fields (MMSI, callsign, flag, home port, hull type, dimensions, roles) - VesselRepository: in-memory CRUD for own vessel, fleet, and crew; getOwnVessel(), getSkipper(), getCrewByRole() helpers - VesselRegistryFragment: own-vessel card + crew list + known-vessels list; AlertDialog editors for add/edit; long-press to delete - ic_vessel.xml sailboat icon; 5th bottom-nav tab "Vessel" - ThermalMonitor: thermalFlow() using Intent.ACTION_BATTERY_CHANGED (battery °C) + PowerManager.OnThermalStatusChangedListener (API 29+); ThermalState OK/WARM/HOT/CRITICAL with distinct-until-changed filter - MainActivity: startThermalMonitoring() sounds mob_alarm + Toast on HOT (≥45°C) and CRITICAL (≥50°C); auto-silences on OK/WARM - MainViewModel: thermalState and thermalReading StateFlows - 18 VesselRepositoryTest cases — all GREEN (test-runner) - design.md: updated competitive matrix + feature implementation status https://claude.ai/code/session_011h2dXbgXg3PesQMmQUNTCW --- .../org/terst/nav/vessel/VesselRepositoryTest.kt | 154 +++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 test-runner/src/test/kotlin/org/terst/nav/vessel/VesselRepositoryTest.kt (limited to 'test-runner/src/test/kotlin') diff --git a/test-runner/src/test/kotlin/org/terst/nav/vessel/VesselRepositoryTest.kt b/test-runner/src/test/kotlin/org/terst/nav/vessel/VesselRepositoryTest.kt new file mode 100644 index 0000000..cec2a54 --- /dev/null +++ b/test-runner/src/test/kotlin/org/terst/nav/vessel/VesselRepositoryTest.kt @@ -0,0 +1,154 @@ +package org.terst.nav.vessel + +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test + +class VesselRepositoryTest { + + private lateinit var repo: VesselRepository + + @Before + fun setup() { + repo = VesselRepository() + } + + // --- Vessel tests --- + + @Test + fun `addVessel with blank id assigns a generated id`() { + val v = repo.addVessel(Vessel(id = "", name = "Perseverance")) + assertTrue(v.id.isNotBlank()) + } + + @Test + fun `addVessel with explicit id preserves id`() { + val v = repo.addVessel(Vessel(id = "abc123", name = "Perseverance")) + assertEquals("abc123", v.id) + } + + @Test + fun `getVessels returns all added vessels`() { + repo.addVessel(Vessel(id = "1", name = "Alpha")) + repo.addVessel(Vessel(id = "2", name = "Beta")) + assertEquals(2, repo.getVessels().size) + } + + @Test + fun `getVessel returns correct vessel by id`() { + repo.addVessel(Vessel(id = "1", name = "Alpha")) + repo.addVessel(Vessel(id = "2", name = "Beta")) + assertEquals("Beta", repo.getVessel("2")?.name) + } + + @Test + fun `updateVessel replaces vessel data`() { + repo.addVessel(Vessel(id = "1", name = "Alpha")) + val updated = repo.updateVessel(Vessel(id = "1", name = "Alpha II", loaMeters = 12.5)) + assertTrue(updated) + assertEquals("Alpha II", repo.getVessel("1")?.name) + assertEquals(12.5, repo.getVessel("1")?.loaMeters ?: 0.0, 0.001) + } + + @Test + fun `updateVessel returns false for unknown id`() { + assertFalse(repo.updateVessel(Vessel(id = "nope", name = "Ghost"))) + } + + @Test + fun `removeVessel removes by id`() { + repo.addVessel(Vessel(id = "1", name = "Alpha")) + repo.addVessel(Vessel(id = "2", name = "Beta")) + repo.removeVessel("1") + assertNull(repo.getVessel("1")) + assertEquals(1, repo.getVessels().size) + } + + @Test + fun `getOwnVessel returns only own vessel`() { + repo.addVessel(Vessel(id = "1", name = "Alpha", isOwnVessel = true)) + repo.addVessel(Vessel(id = "2", name = "Beta", isOwnVessel = false)) + assertEquals("Alpha", repo.getOwnVessel()?.name) + } + + @Test + fun `getOwnVessel returns null when none set`() { + repo.addVessel(Vessel(id = "1", name = "Alpha", isOwnVessel = false)) + assertNull(repo.getOwnVessel()) + } + + @Test + fun `addVessel upserts on duplicate id`() { + repo.addVessel(Vessel(id = "1", name = "Alpha")) + repo.addVessel(Vessel(id = "1", name = "Alpha Updated")) + assertEquals(1, repo.getVessels().size) + assertEquals("Alpha Updated", repo.getVessel("1")?.name) + } + + // --- Crew tests --- + + @Test + fun `addCrewMember with blank id assigns generated id`() { + val m = repo.addCrewMember(CrewMember(id = "", name = "Jane Doe")) + assertTrue(m.id.isNotBlank()) + } + + @Test + fun `getCrew returns all crew members`() { + repo.addCrewMember(CrewMember(id = "c1", name = "Alice")) + repo.addCrewMember(CrewMember(id = "c2", name = "Bob")) + assertEquals(2, repo.getCrew().size) + } + + @Test + fun `getSkipper returns skipper crew member`() { + repo.addCrewMember(CrewMember(id = "c1", name = "Alice", role = CrewRole.CREW)) + repo.addCrewMember(CrewMember(id = "c2", name = "Bob", role = CrewRole.SKIPPER)) + assertEquals("Bob", repo.getSkipper()?.name) + } + + @Test + fun `getSkipper returns null when no skipper`() { + repo.addCrewMember(CrewMember(id = "c1", name = "Alice", role = CrewRole.CREW)) + assertNull(repo.getSkipper()) + } + + @Test + fun `updateCrewMember updates role and details`() { + repo.addCrewMember(CrewMember(id = "c1", name = "Alice", role = CrewRole.CREW)) + val ok = repo.updateCrewMember( + CrewMember(id = "c1", name = "Alice", role = CrewRole.FIRST_MATE, emergencyContact = "Bob") + ) + assertTrue(ok) + assertEquals(CrewRole.FIRST_MATE, repo.getCrewMember("c1")?.role) + assertEquals("Bob", repo.getCrewMember("c1")?.emergencyContact) + } + + @Test + fun `removeCrewMember removes by id`() { + repo.addCrewMember(CrewMember(id = "c1", name = "Alice")) + repo.addCrewMember(CrewMember(id = "c2", name = "Bob")) + repo.removeCrewMember("c1") + assertNull(repo.getCrewMember("c1")) + assertEquals(1, repo.getCrew().size) + } + + @Test + fun `getCrewByRole filters correctly`() { + repo.addCrewMember(CrewMember(id = "c1", name = "Alice", role = CrewRole.CREW)) + repo.addCrewMember(CrewMember(id = "c2", name = "Bob", role = CrewRole.WATCH_CAPTAIN)) + repo.addCrewMember(CrewMember(id = "c3", name = "Carol", role = CrewRole.CREW)) + val result = repo.getCrewByRole(CrewRole.CREW) + assertEquals(2, result.size) + assertTrue(result.all { it.role == CrewRole.CREW }) + } + + @Test + fun `vessel registry and crew are independent`() { + repo.addVessel(Vessel(id = "v1", name = "Alpha")) + repo.addCrewMember(CrewMember(id = "c1", name = "Alice")) + repo.removeVessel("v1") + assertEquals(1, repo.getCrew().size) + assertEquals(0, repo.getVessels().size) + } +} -- cgit v1.2.3