@@ -228,7 +228,7 @@ func TestPush_Humanize(t *testing.T) {
228228 }
229229}
230230
231- func TestCreateStack_Success (t * testing.T ) {
231+ func TestSyncStack_NewStack_CreateSuccess (t * testing.T ) {
232232 s := & stack.Stack {
233233 Trunk : stack.BranchRef {Branch : "main" },
234234 Branches : []stack.BranchRef {
@@ -246,7 +246,7 @@ func TestCreateStack_Success(t *testing.T) {
246246 }
247247
248248 cfg , _ , errR := config .NewTestConfig ()
249- createStack (cfg , mock , s )
249+ syncStack (cfg , mock , s )
250250
251251 cfg .Err .Close ()
252252 errOut , _ := io .ReadAll (errR )
@@ -257,9 +257,109 @@ func TestCreateStack_Success(t *testing.T) {
257257 assert .Contains (t , output , "Stack created on GitHub with 2 PRs" )
258258}
259259
260- func TestCreateStack_AlreadyExists_WithLocalID (t * testing.T ) {
260+ func TestSyncStack_ExistingStack_UpdateSuccess (t * testing.T ) {
261261 s := & stack.Stack {
262262 ID : "99" ,
263+ Trunk : stack.BranchRef {Branch : "main" },
264+ Branches : []stack.BranchRef {
265+ {Branch : "b1" , PullRequest : & stack.PullRequestRef {Number : 10 }},
266+ {Branch : "b2" , PullRequest : & stack.PullRequestRef {Number : 11 }},
267+ {Branch : "b3" , PullRequest : & stack.PullRequestRef {Number : 12 }},
268+ },
269+ }
270+
271+ var gotStackID string
272+ var gotNumbers []int
273+ createCalled := false
274+ mock := & github.MockClient {
275+ CreateStackFn : func ([]int ) (int , error ) {
276+ createCalled = true
277+ return 0 , nil
278+ },
279+ UpdateStackFn : func (stackID string , prNumbers []int ) error {
280+ gotStackID = stackID
281+ gotNumbers = prNumbers
282+ return nil
283+ },
284+ }
285+
286+ cfg , _ , errR := config .NewTestConfig ()
287+ syncStack (cfg , mock , s )
288+
289+ cfg .Err .Close ()
290+ errOut , _ := io .ReadAll (errR )
291+ output := string (errOut )
292+
293+ assert .False (t , createCalled , "CreateStack should not be called when s.ID is set" )
294+ assert .Equal (t , "99" , gotStackID )
295+ assert .Equal (t , []int {10 , 11 , 12 }, gotNumbers )
296+ assert .Contains (t , output , "Stack updated on GitHub with 3 PRs" )
297+ }
298+
299+ func TestSyncStack_ExistingStack_UpdateFails (t * testing.T ) {
300+ s := & stack.Stack {
301+ ID : "99" ,
302+ Trunk : stack.BranchRef {Branch : "main" },
303+ Branches : []stack.BranchRef {
304+ {Branch : "b1" , PullRequest : & stack.PullRequestRef {Number : 10 }},
305+ {Branch : "b2" , PullRequest : & stack.PullRequestRef {Number : 11 }},
306+ },
307+ }
308+
309+ mock := & github.MockClient {
310+ UpdateStackFn : func (string , []int ) error {
311+ return & api.HTTPError {
312+ StatusCode : 422 ,
313+ Message : "Validation failed" ,
314+ RequestURL : & url.URL {Path : "/repos/o/r/cli_internal/pulls/stacks/99" },
315+ }
316+ },
317+ }
318+
319+ cfg , _ , errR := config .NewTestConfig ()
320+ syncStack (cfg , mock , s )
321+
322+ cfg .Err .Close ()
323+ errOut , _ := io .ReadAll (errR )
324+ output := string (errOut )
325+
326+ assert .Contains (t , output , "Failed to update stack" )
327+ }
328+
329+ func TestSyncStack_ExistingStack_Update404 (t * testing.T ) {
330+ s := & stack.Stack {
331+ ID : "99" ,
332+ Trunk : stack.BranchRef {Branch : "main" },
333+ Branches : []stack.BranchRef {
334+ {Branch : "b1" , PullRequest : & stack.PullRequestRef {Number : 10 }},
335+ {Branch : "b2" , PullRequest : & stack.PullRequestRef {Number : 11 }},
336+ },
337+ }
338+
339+ mock := & github.MockClient {
340+ UpdateStackFn : func (string , []int ) error {
341+ return & api.HTTPError {
342+ StatusCode : 404 ,
343+ Message : "Not Found" ,
344+ RequestURL : & url.URL {Path : "/repos/o/r/cli_internal/pulls/stacks/99" },
345+ }
346+ },
347+ }
348+
349+ cfg , _ , errR := config .NewTestConfig ()
350+ syncStack (cfg , mock , s )
351+
352+ cfg .Err .Close ()
353+ errOut , _ := io .ReadAll (errR )
354+ output := string (errOut )
355+
356+ assert .Contains (t , output , "Stack not found on GitHub" )
357+ assert .Contains (t , output , "may have been deleted" )
358+ assert .Equal (t , "" , s .ID , "stale ID should be cleared so next push creates a new stack" )
359+ }
360+
361+ func TestSyncStack_AlreadyStacked_422 (t * testing.T ) {
362+ s := & stack.Stack {
263363 Trunk : stack.BranchRef {Branch : "main" },
264364 Branches : []stack.BranchRef {
265365 {Branch : "b1" , PullRequest : & stack.PullRequestRef {Number : 10 }},
@@ -271,24 +371,24 @@ func TestCreateStack_AlreadyExists_WithLocalID(t *testing.T) {
271371 CreateStackFn : func ([]int ) (int , error ) {
272372 return 0 , & api.HTTPError {
273373 StatusCode : 422 ,
274- Message : "Pull requests are already in a stack " ,
374+ Message : "Pull requests #10, #11 are already stacked " ,
275375 RequestURL : & url.URL {Path : "/repos/o/r/cli_internal/pulls/stacks" },
276376 }
277377 },
278378 }
279379
280380 cfg , _ , errR := config .NewTestConfig ()
281- createStack (cfg , mock , s )
381+ syncStack (cfg , mock , s )
282382
283383 cfg .Err .Close ()
284384 errOut , _ := io .ReadAll (errR )
285385 output := string (errOut )
286386
287- assert .Contains (t , output , "Stack already exists on GitHub " )
288- assert .Equal (t , "99" , s . ID )
387+ assert .Contains (t , output , "already stacked " )
388+ assert .Contains (t , output , "Updating existing stacks will be supported in an upcoming release" )
289389}
290390
291- func TestCreateStack_AlreadyExists_NoLocalID (t * testing.T ) {
391+ func TestSyncStack_InvalidChain_422 (t * testing.T ) {
292392 s := & stack.Stack {
293393 Trunk : stack.BranchRef {Branch : "main" },
294394 Branches : []stack.BranchRef {
@@ -301,24 +401,24 @@ func TestCreateStack_AlreadyExists_NoLocalID(t *testing.T) {
301401 CreateStackFn : func ([]int ) (int , error ) {
302402 return 0 , & api.HTTPError {
303403 StatusCode : 422 ,
304- Message : "Pull requests are already in a stack" ,
404+ Message : "Pull requests must form a stack, where each PR's base ref is the previous PR's head ref " ,
305405 RequestURL : & url.URL {Path : "/repos/o/r/cli_internal/pulls/stacks" },
306406 }
307407 },
308408 }
309409
310410 cfg , _ , errR := config .NewTestConfig ()
311- createStack (cfg , mock , s )
411+ syncStack (cfg , mock , s )
312412
313413 cfg .Err .Close ()
314414 errOut , _ := io .ReadAll (errR )
315415 output := string (errOut )
316416
317- assert .Contains (t , output , "Could not create stack" )
318- assert .Contains (t , output , "Updating existing stacks will be supported in an upcoming release " )
417+ assert .Contains (t , output , "must form a stack" )
418+ assert .Contains (t , output , "base branch must match " )
319419}
320420
321- func TestCreateStack_NotAvailable (t * testing.T ) {
421+ func TestSyncStack_NotAvailable (t * testing.T ) {
322422 s := & stack.Stack {
323423 Trunk : stack.BranchRef {Branch : "main" },
324424 Branches : []stack.BranchRef {
@@ -338,7 +438,7 @@ func TestCreateStack_NotAvailable(t *testing.T) {
338438 }
339439
340440 cfg , _ , errR := config .NewTestConfig ()
341- createStack (cfg , mock , s )
441+ syncStack (cfg , mock , s )
342442
343443 cfg .Err .Close ()
344444 errOut , _ := io .ReadAll (errR )
@@ -347,30 +447,36 @@ func TestCreateStack_NotAvailable(t *testing.T) {
347447 assert .Contains (t , output , "not yet available" )
348448}
349449
350- func TestCreateStack_SkippedForSinglePR (t * testing.T ) {
450+ func TestSyncStack_SkippedForSinglePR (t * testing.T ) {
351451 s := & stack.Stack {
352452 Trunk : stack.BranchRef {Branch : "main" },
353453 Branches : []stack.BranchRef {
354454 {Branch : "b1" , PullRequest : & stack.PullRequestRef {Number : 10 }},
355455 },
356456 }
357457
358- called := false
458+ createCalled := false
459+ updateCalled := false
359460 mock := & github.MockClient {
360461 CreateStackFn : func ([]int ) (int , error ) {
361- called = true
462+ createCalled = true
362463 return 42 , nil
363464 },
465+ UpdateStackFn : func (string , []int ) error {
466+ updateCalled = true
467+ return nil
468+ },
364469 }
365470
366471 cfg , _ , _ := config .NewTestConfig ()
367- createStack (cfg , mock , s )
472+ syncStack (cfg , mock , s )
368473 cfg .Err .Close ()
369474
370- assert .False (t , called , "CreateStack should not be called with fewer than 2 PRs" )
475+ assert .False (t , createCalled , "CreateStack should not be called with fewer than 2 PRs" )
476+ assert .False (t , updateCalled , "UpdateStack should not be called with fewer than 2 PRs" )
371477}
372478
373- func TestCreateStack_SkipsMergedBranches (t * testing.T ) {
479+ func TestSyncStack_SkipsMergedBranches (t * testing.T ) {
374480 s := & stack.Stack {
375481 Trunk : stack.BranchRef {Branch : "main" },
376482 Branches : []stack.BranchRef {
@@ -389,13 +495,13 @@ func TestCreateStack_SkipsMergedBranches(t *testing.T) {
389495 }
390496
391497 cfg , _ , _ := config .NewTestConfig ()
392- createStack (cfg , mock , s )
498+ syncStack (cfg , mock , s )
393499 cfg .Err .Close ()
394500
395501 assert .Equal (t , []int {11 , 12 }, gotNumbers , "should only include non-merged PRs" )
396502}
397503
398- func TestCreateStack_SkipsBranchesWithoutPR (t * testing.T ) {
504+ func TestSyncStack_SkipsBranchesWithoutPR (t * testing.T ) {
399505 s := & stack.Stack {
400506 Trunk : stack.BranchRef {Branch : "main" },
401507 Branches : []stack.BranchRef {
@@ -414,7 +520,7 @@ func TestCreateStack_SkipsBranchesWithoutPR(t *testing.T) {
414520 }
415521
416522 cfg , _ , _ := config .NewTestConfig ()
417- createStack (cfg , mock , s )
523+ syncStack (cfg , mock , s )
418524 cfg .Err .Close ()
419525
420526 assert .Equal (t , []int {10 , 12 }, gotNumbers , "should skip branches without PRs" )
0 commit comments