@@ -106,6 +106,26 @@ describe("BuiltInStrategies", () => {
106
106
expect ( BuiltInStrategies . update ( { ...args , ours : undefined , theirs : "y" } ) . value ) . toBe ( DROP ) ;
107
107
} ) ;
108
108
109
+ it ( "concat arrays" , ( ) => {
110
+ const r = BuiltInStrategies . concat ( { ...args , ours : [ 1 ] , theirs : [ 2 ] } ) ;
111
+ expect ( r ) . toEqual ( { status : StrategyStatus_OK , value : [ 1 , 2 ] } ) ;
112
+ } ) ;
113
+
114
+ it ( "concat non-arrays → CONTINUE" , ( ) => {
115
+ const r = BuiltInStrategies . concat ( { ...args , ours : "a" , theirs : "b" } ) ;
116
+ expect ( r . status ) . toBe ( StrategyStatus_CONTINUE ) ;
117
+ } ) ;
118
+
119
+ it ( "unique arrays" , ( ) => {
120
+ const r = BuiltInStrategies . unique ( { ...args , ours : [ 1 , 2 ] , theirs : [ 2 , 3 ] } ) ;
121
+ expect ( r ) . toEqual ( { status : StrategyStatus_OK , value : [ 1 , 2 , 3 ] } ) ;
122
+ } ) ;
123
+
124
+ it ( "unique non-arrays → CONTINUE" , ( ) => {
125
+ const r = BuiltInStrategies . unique ( { ...args , ours : "a" , theirs : "b" } ) ;
126
+ expect ( r . status ) . toBe ( StrategyStatus_CONTINUE ) ;
127
+ } ) ;
128
+
109
129
it ( "merge plain objects recurses" , async ( ) => {
110
130
const objArgs = {
111
131
...args ,
@@ -175,13 +195,13 @@ describe("mergeObject", () => {
175
195
expect ( conflicts [ 0 ] . reason ) . toMatch ( / S k i p / ) ;
176
196
} ) ;
177
197
178
- it . skip ( "adds conflict if all CONTINUE" , async ( ) => {
179
- ( resolveStrategies as any ) . mockReturnValueOnce ( [ "non-empty " ] ) ;
198
+ it ( "adds conflict if all CONTINUE" , async ( ) => {
199
+ ( resolveStrategies as any ) . mockReturnValueOnce ( [ "concat " ] ) ;
180
200
const ctx = makeCtx ( ) ;
181
201
const conflicts : Conflict [ ] = [ ] ;
182
202
const v = await mergeObject ( {
183
- ours : "" ,
184
- theirs : "" ,
203
+ ours : "a " ,
204
+ theirs : "b " ,
185
205
base : "" ,
186
206
path : "p" ,
187
207
ctx,
@@ -194,4 +214,64 @@ describe("mergeObject", () => {
194
214
reason : expect . stringContaining ( "All strategies failed" ) ,
195
215
} ) ;
196
216
} ) ;
197
- } ) ;
217
+
218
+ it ( "uses custom strategy from ctx.strategies" , async ( ) => {
219
+ ( resolveStrategies as any ) . mockReturnValueOnce ( [ "custom" ] ) ;
220
+ const ctx = makeCtx ( ) ;
221
+ ctx . strategies . custom = vi . fn ( ( ) => ( { status : StrategyStatus_OK , value : "custom-result" } ) ) ;
222
+ const conflicts : Conflict [ ] = [ ] ;
223
+ const v = await mergeObject ( {
224
+ ours : "a" ,
225
+ theirs : "b" ,
226
+ path : "p" ,
227
+ ctx,
228
+ conflicts,
229
+ logger : mockLogger ,
230
+ } ) ;
231
+ expect ( v ) . toBe ( "custom-result" ) ;
232
+ expect ( ctx . strategies . custom ) . toHaveBeenCalled ( ) ;
233
+ } ) ;
234
+
235
+ it ( "throws on FAIL status" , async ( ) => {
236
+ ( resolveStrategies as any ) . mockReturnValueOnce ( [ "fail-strategy" ] ) ;
237
+ const ctx = makeCtx ( ) ;
238
+ ctx . strategies [ "fail-strategy" ] = vi . fn ( ( ) => ( {
239
+ status : StrategyStatus_FAIL ,
240
+ reason : "test fail" ,
241
+ } ) ) ;
242
+ const conflicts : Conflict [ ] = [ ] ;
243
+ await expect (
244
+ mergeObject ( {
245
+ ours : "a" ,
246
+ theirs : "b" ,
247
+ path : "p" ,
248
+ ctx,
249
+ conflicts,
250
+ logger : mockLogger ,
251
+ } ) ,
252
+ ) . rejects . toThrow ( "Merge failed at p: test fail" ) ;
253
+ expect ( conflicts [ 0 ] . reason ) . toBe ( "test fail" ) ;
254
+ } ) ;
255
+
256
+ it ( "enriches conflict with debug info when debug enabled" , async ( ) => {
257
+ ( resolveStrategies as any ) . mockReturnValueOnce ( [ "concat" ] ) ;
258
+ const ctx = makeCtx ( ) ;
259
+ ctx . config . debug = true ;
260
+ const conflicts : Conflict [ ] = [ ] ;
261
+ await mergeObject ( {
262
+ ours : "a" ,
263
+ theirs : "b" ,
264
+ base : "c" ,
265
+ path : "p" ,
266
+ ctx,
267
+ conflicts,
268
+ logger : mockLogger ,
269
+ } ) ;
270
+ expect ( conflicts [ 0 ] ) . toMatchObject ( {
271
+ path : "p" ,
272
+ ours : "a" ,
273
+ theirs : "b" ,
274
+ base : "c" ,
275
+ } ) ;
276
+ } ) ;
277
+ } ) ;
0 commit comments