1+ package app.morphe.cli.command
2+
3+ import app.morphe.cli.command.model.PatchBundle
4+ import app.morphe.cli.command.model.PatchBundleMeta
5+ import app.morphe.cli.command.model.PatchEntry
6+ import app.morphe.cli.command.model.findMatchingBundle
7+ import app.morphe.cli.command.model.mergeWithBundle
8+ import app.morphe.cli.command.model.sha256
9+ import app.morphe.cli.command.model.withUpdatedBundle
10+ import app.morphe.patcher.patch.option
11+ import app.morphe.patcher.patch.rawResourcePatch
12+ import kotlinx.serialization.json.JsonPrimitive
13+ import java.io.File
14+ import kotlin.test.Test
15+ import kotlin.test.assertEquals
16+ import kotlin.test.assertNull
17+
18+ class PatchOptionsFileTest {
19+ // findMatchingBundle section ------------------
20+ @Test
21+ fun `findMatchingBundle returns null for empty list` (){
22+ val result = emptyList<PatchBundle >().findMatchingBundle(emptySet())
23+ assertNull(result)
24+ }
25+
26+ @Test
27+ fun `findMatchingBundle returns single Patch element` (){
28+ val bundle = PatchBundle (
29+ meta = PatchBundleMeta (source = " patches.mpp" ),
30+ patches = mapOf (" Test patch" to PatchEntry (enabled = true ))
31+ )
32+
33+ val result = listOf (bundle).findMatchingBundle(emptySet())
34+ assertEquals(bundle, result)
35+ }
36+
37+ @Test
38+ fun `findMatchingBundle with multiple Patch bundles` (){
39+ val tmpFile = File .createTempFile(" test" , " .mpp" )
40+ tmpFile.writeText(" test content" )
41+ tmpFile.deleteOnExit()
42+ val hash = tmpFile.sha256() // We use SHA to get our exact patch options
43+
44+ val bundle1 = PatchBundle (
45+ meta = PatchBundleMeta (sha256 = " some-wrong-hash" ),
46+ patches = mapOf (" Test patch 1" to PatchEntry (enabled = true ))
47+ )
48+ val bundle2 = PatchBundle (
49+ meta = PatchBundleMeta (sha256 = hash),
50+ patches = mapOf (" Test patch 2" to PatchEntry (enabled = true ))
51+ )
52+
53+ val result = listOf (bundle1,bundle2).findMatchingBundle(setOf (tmpFile))
54+ assertEquals(bundle2, result)
55+ }
56+
57+ // withUpdateBundle section ------------------
58+ @Test
59+ fun `withUpdateBundle replaces matching Bundle` (){
60+ // Replace the old patch with same hash with fresh run's results (Both share the same hash)
61+ val oldBundle = PatchBundle (
62+ meta = PatchBundleMeta (sha256 = " hash-A" ),
63+ patches = mapOf (" Test patch 1" to PatchEntry (enabled = true ))
64+ )
65+
66+ val newBundle = PatchBundle (
67+ meta = PatchBundleMeta (sha256 = " hash-A" ),
68+ patches = mapOf (" Test patch 2" to PatchEntry (enabled = true ))
69+ )
70+
71+ val result = listOf (oldBundle).withUpdatedBundle(newBundle)
72+ assertEquals(1 , result.size)
73+ assertEquals(newBundle, result[0 ])
74+ }
75+
76+ @Test
77+ fun `withUpdateBundle appends when no matching Bundle` (){
78+ // Add the new patch's results to the end of the file because this is a new patch's results
79+ val oldBundle = PatchBundle (
80+ meta = PatchBundleMeta (sha256 = " hash-A" ),
81+ patches = mapOf (" Test patch 1" to PatchEntry (enabled = true ))
82+ )
83+
84+ val newBundle = PatchBundle (
85+ meta = PatchBundleMeta (sha256 = " hash-B" ),
86+ patches = mapOf (" Test patch 2" to PatchEntry (enabled = true ))
87+ )
88+
89+ val result = listOf (oldBundle).withUpdatedBundle(newBundle)
90+ assertEquals(2 , result.size)
91+ assertEquals(oldBundle, result[0 ])
92+ assertEquals(newBundle, result.last())
93+ }
94+
95+ // mergeWithBundle section ------------------
96+ @Test
97+ fun `mergeWithBundle preserves existing settings` (){
98+ // New patch from .mpp files comes with this default (enabled + light)
99+ val patch = rawResourcePatch(name = " Theme" , description = " Change Theme" , use = true ){
100+ option<String >(key = " colorScheme" , default = " light" )
101+ }
102+
103+ // User's existing bundle has these settings (disabled + dark)
104+ val userBundle = PatchBundle (
105+ meta = PatchBundleMeta (),
106+ patches = mapOf (" Theme" to PatchEntry (
107+ enabled = false ,
108+ options = mapOf (" colorScheme" to JsonPrimitive (" dark" ))
109+ ))
110+ )
111+
112+ // Merge should keep user's settings
113+ val result = setOf (patch).mergeWithBundle(existing = userBundle)
114+
115+ val themeEntry = result.patches[" Theme" ]!!
116+ assertEquals(false , themeEntry.enabled)
117+ assertEquals(JsonPrimitive (" dark" ), themeEntry.options[" colorScheme" ])
118+ }
119+
120+ @Test
121+ fun `mergeWithBundle adds new Patch that didn't exist with default settings` (){
122+ // We add new Patch that didn't exist with default values
123+ val themePatch = rawResourcePatch(name = " Theme" , description = " Change Theme" , use = true ) {}
124+
125+ val adBlockPatch = rawResourcePatch(name = " AdBlocker" , description = " Block Ads" , use = true ) {}
126+
127+ val userBundle = PatchBundle (
128+ meta = PatchBundleMeta (),
129+ patches = mapOf (" Theme" to PatchEntry (enabled = false ))
130+ )
131+ val result = setOf (themePatch,adBlockPatch).mergeWithBundle(existing = userBundle)
132+ assertEquals(2 , result.patches.size)
133+ assertEquals(false , result.patches[" Theme" ]!! .enabled) // This is preserved
134+ assertEquals(true , result.patches[" AdBlocker" ]!! .enabled) // Added with default setting
135+ }
136+
137+ @Test
138+ fun `mergeWithBundle removes an old patch that no longer exists` (){
139+ // We remove an old patch that no longer exists
140+ val themePatch = rawResourcePatch(name = " Theme" , description = " Change Theme" , use = true ) {}
141+
142+ val userBundle = PatchBundle (
143+ meta = PatchBundleMeta (),
144+ patches = mapOf (
145+ " Theme" to PatchEntry (enabled = false ),
146+ " AdBlocker" to PatchEntry (enabled = true )
147+ )
148+ )
149+
150+ val result = setOf (themePatch).mergeWithBundle(existing = userBundle)
151+ assertEquals(1 , result.patches.size)
152+ assertEquals(false , result.patches[" Theme" ]!! .enabled)
153+ assertNull(result.patches[" AdBlocker" ])
154+ }
155+ }
0 commit comments