mirror of
https://github.com/github/gh-stack.git
synced 2026-09-14 20:26:28 +08:00
38aba1b710
* deprecate prefix functionality * clean up branch auto-naming * update docs * preserve literal hyphens when slugifying branch names
659 lines
16 KiB
Go
659 lines
16 KiB
Go
package stack
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func makeStack(trunk string, branches ...string) Stack {
|
|
s := Stack{Trunk: BranchRef{Branch: trunk}}
|
|
for _, b := range branches {
|
|
s.Branches = append(s.Branches, BranchRef{Branch: b})
|
|
}
|
|
return s
|
|
}
|
|
|
|
func makeMergedBranch(name string, prNum int) BranchRef {
|
|
return BranchRef{Branch: name, PullRequest: &PullRequestRef{Number: prNum, Merged: true}}
|
|
}
|
|
|
|
// --- ActiveBaseBranch: skipping merged ancestors for rebase ---
|
|
|
|
func TestActiveBaseBranch(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
stack Stack
|
|
branch string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "no merged ancestors returns previous branch",
|
|
stack: Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
{Branch: "b1"},
|
|
{Branch: "b2"},
|
|
{Branch: "b3"},
|
|
},
|
|
},
|
|
branch: "b3",
|
|
expected: "b2",
|
|
},
|
|
{
|
|
name: "immediate ancestor merged skips to next non-merged",
|
|
stack: Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
{Branch: "b1"},
|
|
makeMergedBranch("b2", 10),
|
|
{Branch: "b3"},
|
|
},
|
|
},
|
|
branch: "b3",
|
|
expected: "b1",
|
|
},
|
|
{
|
|
name: "all ancestors merged returns trunk",
|
|
stack: Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeMergedBranch("b1", 1),
|
|
makeMergedBranch("b2", 2),
|
|
{Branch: "b3"},
|
|
},
|
|
},
|
|
branch: "b3",
|
|
expected: "main",
|
|
},
|
|
{
|
|
name: "first branch always returns trunk",
|
|
stack: Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{{Branch: "b1"}},
|
|
},
|
|
branch: "b1",
|
|
expected: "main",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assert.Equal(t, tt.expected, tt.stack.ActiveBaseBranch(tt.branch))
|
|
})
|
|
}
|
|
}
|
|
|
|
// --- ActiveBranches / MergedBranches partition ---
|
|
|
|
func TestActiveBranches_And_MergedBranches(t *testing.T) {
|
|
t.Run("all active", func(t *testing.T) {
|
|
s := makeStack("main", "b1", "b2", "b3")
|
|
assert.Len(t, s.ActiveBranches(), 3)
|
|
assert.Empty(t, s.MergedBranches())
|
|
})
|
|
|
|
t.Run("some merged", func(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeMergedBranch("b1", 1),
|
|
{Branch: "b2"},
|
|
makeMergedBranch("b3", 3),
|
|
},
|
|
}
|
|
active := s.ActiveBranches()
|
|
merged := s.MergedBranches()
|
|
|
|
assert.Len(t, active, 1)
|
|
assert.Equal(t, "b2", active[0].Branch)
|
|
assert.Len(t, merged, 2)
|
|
assert.Equal(t, "b1", merged[0].Branch)
|
|
assert.Equal(t, "b3", merged[1].Branch)
|
|
})
|
|
|
|
t.Run("all merged", func(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeMergedBranch("b1", 1),
|
|
makeMergedBranch("b2", 2),
|
|
},
|
|
}
|
|
assert.Empty(t, s.ActiveBranches())
|
|
assert.Len(t, s.MergedBranches(), 2)
|
|
})
|
|
}
|
|
|
|
// --- IsFullyMerged: blocks add on fully-merged stacks ---
|
|
|
|
func TestIsFullyMerged(t *testing.T) {
|
|
t.Run("all merged", func(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeMergedBranch("b1", 1),
|
|
makeMergedBranch("b2", 2),
|
|
},
|
|
}
|
|
assert.True(t, s.IsFullyMerged())
|
|
})
|
|
|
|
t.Run("some active", func(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeMergedBranch("b1", 1),
|
|
{Branch: "b2"},
|
|
},
|
|
}
|
|
assert.False(t, s.IsFullyMerged())
|
|
})
|
|
|
|
t.Run("empty branches is not fully merged", func(t *testing.T) {
|
|
s := Stack{Trunk: BranchRef{Branch: "main"}}
|
|
assert.False(t, s.IsFullyMerged())
|
|
})
|
|
}
|
|
|
|
// --- FirstActiveBranchIndex: navigation ---
|
|
|
|
func TestFirstActiveBranchIndex(t *testing.T) {
|
|
t.Run("first is active", func(t *testing.T) {
|
|
s := makeStack("main", "b1", "b2")
|
|
assert.Equal(t, 0, s.FirstActiveBranchIndex())
|
|
})
|
|
|
|
t.Run("first two merged third active", func(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeMergedBranch("b1", 1),
|
|
makeMergedBranch("b2", 2),
|
|
{Branch: "b3"},
|
|
},
|
|
}
|
|
assert.Equal(t, 2, s.FirstActiveBranchIndex())
|
|
})
|
|
|
|
t.Run("all merged", func(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeMergedBranch("b1", 1),
|
|
makeMergedBranch("b2", 2),
|
|
},
|
|
}
|
|
assert.Equal(t, -1, s.FirstActiveBranchIndex())
|
|
})
|
|
}
|
|
|
|
// --- ActiveBranchIndices: navigation ---
|
|
|
|
func TestActiveBranchIndices(t *testing.T) {
|
|
t.Run("all active", func(t *testing.T) {
|
|
s := makeStack("main", "b1", "b2", "b3")
|
|
assert.Equal(t, []int{0, 1, 2}, s.ActiveBranchIndices())
|
|
})
|
|
|
|
t.Run("some merged", func(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeMergedBranch("b1", 1),
|
|
{Branch: "b2"},
|
|
makeMergedBranch("b3", 3),
|
|
{Branch: "b4"},
|
|
},
|
|
}
|
|
assert.Equal(t, []int{1, 3}, s.ActiveBranchIndices())
|
|
})
|
|
|
|
t.Run("all merged", func(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeMergedBranch("b1", 1),
|
|
makeMergedBranch("b2", 2),
|
|
},
|
|
}
|
|
assert.Empty(t, s.ActiveBranchIndices())
|
|
})
|
|
}
|
|
|
|
// --- Load / Save round-trip persistence ---
|
|
|
|
func TestLoad_Save_RoundTrip(t *testing.T) {
|
|
t.Run("save and reload preserves all fields", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
original := &StackFile{
|
|
Repository: "owner/repo",
|
|
Stacks: []Stack{
|
|
{
|
|
ID: "s1",
|
|
Number: 7,
|
|
Trunk: BranchRef{Branch: "main", Head: "abc123"},
|
|
Branches: []BranchRef{
|
|
{Branch: "b1", Head: "def456", Base: "abc123"},
|
|
{Branch: "b2", PullRequest: &PullRequestRef{Number: 42, ID: "PR_id", URL: "https://example.com", Merged: true}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
require.NoError(t, Save(dir, original))
|
|
|
|
loaded, err := Load(dir)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, schemaVersion, loaded.SchemaVersion)
|
|
assert.Equal(t, original.Repository, loaded.Repository)
|
|
require.Len(t, loaded.Stacks, 1)
|
|
|
|
s := loaded.Stacks[0]
|
|
assert.Equal(t, "s1", s.ID)
|
|
assert.Equal(t, 7, s.Number)
|
|
assert.Equal(t, "main", s.Trunk.Branch)
|
|
assert.Equal(t, "abc123", s.Trunk.Head)
|
|
require.Len(t, s.Branches, 2)
|
|
assert.Equal(t, "b1", s.Branches[0].Branch)
|
|
assert.Equal(t, "def456", s.Branches[0].Head)
|
|
assert.Equal(t, "abc123", s.Branches[0].Base)
|
|
require.NotNil(t, s.Branches[1].PullRequest)
|
|
assert.Equal(t, 42, s.Branches[1].PullRequest.Number)
|
|
assert.True(t, s.Branches[1].PullRequest.Merged)
|
|
})
|
|
|
|
t.Run("missing file returns empty stack file", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
sf, err := Load(dir)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, schemaVersion, sf.SchemaVersion)
|
|
assert.Empty(t, sf.Stacks)
|
|
})
|
|
|
|
t.Run("future schema version returns error", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
data, _ := json.Marshal(StackFile{SchemaVersion: 999})
|
|
require.NoError(t, os.WriteFile(filepath.Join(dir, stackFileName), data, 0644))
|
|
|
|
_, err := Load(dir)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "999")
|
|
})
|
|
|
|
t.Run("corrupt JSON returns error", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
require.NoError(t, os.WriteFile(filepath.Join(dir, stackFileName), []byte("{not json!"), 0644))
|
|
|
|
_, err := Load(dir)
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
// --- FindStackByPRNumber: used by checkout ---
|
|
|
|
func TestFindStackByPRNumber(t *testing.T) {
|
|
sf := &StackFile{
|
|
Stacks: []Stack{
|
|
{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
{Branch: "b1", PullRequest: &PullRequestRef{Number: 10}},
|
|
{Branch: "b2", PullRequest: &PullRequestRef{Number: 20}},
|
|
},
|
|
},
|
|
{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
{Branch: "b3", PullRequest: &PullRequestRef{Number: 30}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
t.Run("found", func(t *testing.T) {
|
|
s, b := sf.FindStackByPRNumber(20)
|
|
require.NotNil(t, s)
|
|
require.NotNil(t, b)
|
|
assert.Equal(t, "b2", b.Branch)
|
|
})
|
|
|
|
t.Run("found in second stack", func(t *testing.T) {
|
|
s, b := sf.FindStackByPRNumber(30)
|
|
require.NotNil(t, s)
|
|
require.NotNil(t, b)
|
|
assert.Equal(t, "b3", b.Branch)
|
|
})
|
|
|
|
t.Run("not found", func(t *testing.T) {
|
|
s, b := sf.FindStackByPRNumber(999)
|
|
assert.Nil(t, s)
|
|
assert.Nil(t, b)
|
|
})
|
|
}
|
|
|
|
// --- ValidateNoDuplicateBranch: guards against duplicates ---
|
|
|
|
func TestValidateNoDuplicateBranch(t *testing.T) {
|
|
sf := &StackFile{
|
|
Stacks: []Stack{
|
|
makeStack("main", "b1", "b2"),
|
|
},
|
|
}
|
|
|
|
t.Run("branch in stack returns error", func(t *testing.T) {
|
|
assert.Error(t, sf.ValidateNoDuplicateBranch("b1"))
|
|
})
|
|
|
|
t.Run("trunk returns error because Contains checks trunk", func(t *testing.T) {
|
|
assert.Error(t, sf.ValidateNoDuplicateBranch("main"))
|
|
})
|
|
|
|
t.Run("new branch returns nil", func(t *testing.T) {
|
|
assert.NoError(t, sf.ValidateNoDuplicateBranch("new-branch"))
|
|
})
|
|
}
|
|
|
|
// --- RemoveStackForBranch: used by unstack ---
|
|
|
|
func TestRemoveStackForBranch(t *testing.T) {
|
|
t.Run("found and removed", func(t *testing.T) {
|
|
sf := &StackFile{
|
|
Stacks: []Stack{
|
|
makeStack("main", "b1"),
|
|
makeStack("main", "b2"),
|
|
},
|
|
}
|
|
assert.True(t, sf.RemoveStackForBranch("b1"))
|
|
require.Len(t, sf.Stacks, 1)
|
|
assert.Equal(t, "b2", sf.Stacks[0].Branches[0].Branch)
|
|
})
|
|
|
|
t.Run("not found", func(t *testing.T) {
|
|
sf := &StackFile{
|
|
Stacks: []Stack{makeStack("main", "b1")},
|
|
}
|
|
assert.False(t, sf.RemoveStackForBranch("nonexistent"))
|
|
assert.Len(t, sf.Stacks, 1)
|
|
})
|
|
}
|
|
|
|
// --- IndexOfStack: locating a stack by identity ---
|
|
|
|
func TestIndexOfStack(t *testing.T) {
|
|
sf := &StackFile{
|
|
Stacks: []Stack{
|
|
makeStack("main", "a1"),
|
|
makeStack("main", "b1"),
|
|
makeStack("main", "c1"),
|
|
},
|
|
}
|
|
|
|
assert.Equal(t, 0, sf.IndexOfStack(&sf.Stacks[0]))
|
|
assert.Equal(t, 2, sf.IndexOfStack(&sf.Stacks[2]))
|
|
|
|
t.Run("not part of the file", func(t *testing.T) {
|
|
other := makeStack("main", "z1")
|
|
assert.Equal(t, -1, sf.IndexOfStack(&other))
|
|
})
|
|
}
|
|
|
|
// --- Queued state: transient merge queue support ---
|
|
|
|
func makeQueuedBranch(name string, prNum int) BranchRef {
|
|
return BranchRef{
|
|
Branch: name,
|
|
PullRequest: &PullRequestRef{Number: prNum},
|
|
Queued: true,
|
|
}
|
|
}
|
|
|
|
func TestIsQueued(t *testing.T) {
|
|
t.Run("queued branch", func(t *testing.T) {
|
|
b := makeQueuedBranch("b1", 1)
|
|
assert.True(t, b.IsQueued())
|
|
assert.False(t, b.IsMerged())
|
|
assert.True(t, b.IsSkipped())
|
|
})
|
|
|
|
t.Run("merged branch", func(t *testing.T) {
|
|
b := makeMergedBranch("b1", 1)
|
|
assert.False(t, b.IsQueued())
|
|
assert.True(t, b.IsMerged())
|
|
assert.True(t, b.IsSkipped())
|
|
})
|
|
|
|
t.Run("active branch", func(t *testing.T) {
|
|
b := BranchRef{Branch: "b1"}
|
|
assert.False(t, b.IsQueued())
|
|
assert.False(t, b.IsMerged())
|
|
assert.False(t, b.IsSkipped())
|
|
})
|
|
}
|
|
|
|
func TestQueuedBranches(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
{Branch: "b1"},
|
|
makeQueuedBranch("b2", 2),
|
|
{Branch: "b3"},
|
|
makeQueuedBranch("b4", 4),
|
|
},
|
|
}
|
|
queued := s.QueuedBranches()
|
|
assert.Len(t, queued, 2)
|
|
assert.Equal(t, "b2", queued[0].Branch)
|
|
assert.Equal(t, "b4", queued[1].Branch)
|
|
}
|
|
|
|
func TestActiveBranches_ExcludesQueued(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeQueuedBranch("b1", 1),
|
|
{Branch: "b2"},
|
|
makeMergedBranch("b3", 3),
|
|
{Branch: "b4"},
|
|
},
|
|
}
|
|
active := s.ActiveBranches()
|
|
assert.Len(t, active, 2)
|
|
assert.Equal(t, "b2", active[0].Branch)
|
|
assert.Equal(t, "b4", active[1].Branch)
|
|
}
|
|
|
|
func TestFirstActiveBranchIndex_SkipsQueued(t *testing.T) {
|
|
t.Run("queued first, then active", func(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeQueuedBranch("b1", 1),
|
|
{Branch: "b2"},
|
|
},
|
|
}
|
|
assert.Equal(t, 1, s.FirstActiveBranchIndex())
|
|
})
|
|
|
|
t.Run("all queued", func(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeQueuedBranch("b1", 1),
|
|
makeQueuedBranch("b2", 2),
|
|
},
|
|
}
|
|
assert.Equal(t, -1, s.FirstActiveBranchIndex())
|
|
})
|
|
|
|
t.Run("merged then queued then active", func(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeMergedBranch("b1", 1),
|
|
makeQueuedBranch("b2", 2),
|
|
{Branch: "b3"},
|
|
},
|
|
}
|
|
assert.Equal(t, 2, s.FirstActiveBranchIndex())
|
|
})
|
|
}
|
|
|
|
func TestActiveBranchIndices_SkipsQueued(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeQueuedBranch("b1", 1),
|
|
{Branch: "b2"},
|
|
makeMergedBranch("b3", 3),
|
|
{Branch: "b4"},
|
|
makeQueuedBranch("b5", 5),
|
|
},
|
|
}
|
|
assert.Equal(t, []int{1, 3}, s.ActiveBranchIndices())
|
|
}
|
|
|
|
func TestActiveBaseBranch_SkipsQueued(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
stack Stack
|
|
branch string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "queued ancestor skipped to trunk",
|
|
stack: Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeQueuedBranch("b1", 1),
|
|
{Branch: "b2"},
|
|
},
|
|
},
|
|
branch: "b2",
|
|
expected: "main",
|
|
},
|
|
{
|
|
name: "queued ancestor skipped to active sibling",
|
|
stack: Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
{Branch: "b1"},
|
|
makeQueuedBranch("b2", 2),
|
|
{Branch: "b3"},
|
|
},
|
|
},
|
|
branch: "b3",
|
|
expected: "b1",
|
|
},
|
|
{
|
|
name: "mixed merged and queued ancestors skip to trunk",
|
|
stack: Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeMergedBranch("b1", 1),
|
|
makeQueuedBranch("b2", 2),
|
|
{Branch: "b3"},
|
|
},
|
|
},
|
|
branch: "b3",
|
|
expected: "main",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assert.Equal(t, tt.expected, tt.stack.ActiveBaseBranch(tt.branch))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestQueuedState_NotPersisted(t *testing.T) {
|
|
dir := t.TempDir()
|
|
original := &StackFile{
|
|
Repository: "owner/repo",
|
|
Stacks: []Stack{
|
|
{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
{
|
|
Branch: "b1",
|
|
PullRequest: &PullRequestRef{Number: 1},
|
|
Queued: true, // set transient state
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
require.NoError(t, Save(dir, original))
|
|
|
|
loaded, err := Load(dir)
|
|
require.NoError(t, err)
|
|
require.Len(t, loaded.Stacks, 1)
|
|
require.Len(t, loaded.Stacks[0].Branches, 1)
|
|
|
|
// Queued state should NOT be persisted (json:"-")
|
|
assert.False(t, loaded.Stacks[0].Branches[0].Queued)
|
|
assert.False(t, loaded.Stacks[0].Branches[0].IsQueued())
|
|
}
|
|
|
|
func TestIsFullyMerged_NotAffectedByQueued(t *testing.T) {
|
|
t.Run("all queued is not fully merged", func(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeQueuedBranch("b1", 1),
|
|
makeQueuedBranch("b2", 2),
|
|
},
|
|
}
|
|
assert.False(t, s.IsFullyMerged())
|
|
})
|
|
|
|
t.Run("merged and queued is not fully merged", func(t *testing.T) {
|
|
s := Stack{
|
|
Trunk: BranchRef{Branch: "main"},
|
|
Branches: []BranchRef{
|
|
makeMergedBranch("b1", 1),
|
|
makeQueuedBranch("b2", 2),
|
|
},
|
|
}
|
|
assert.False(t, s.IsFullyMerged())
|
|
})
|
|
}
|
|
|
|
func TestNearestSurvivingBranch(t *testing.T) {
|
|
// survivesIn returns a predicate reporting membership in the given set.
|
|
survivesIn := func(names ...string) func(string) bool {
|
|
set := make(map[string]bool, len(names))
|
|
for _, n := range names {
|
|
set[n] = true
|
|
}
|
|
return func(name string) bool { return set[name] }
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
order []string
|
|
target string
|
|
survives func(string) bool
|
|
want string
|
|
}{
|
|
{"prefers neighbor above", []string{"a", "b", "c"}, "b", survivesIn("a", "c"), "c"},
|
|
{"falls to neighbor below", []string{"a", "b", "c"}, "c", survivesIn("a", "b"), "b"},
|
|
{"skips dead neighbors above", []string{"a", "b", "c", "d"}, "b", survivesIn("a", "d"), "d"},
|
|
{"target not in order", []string{"a", "b"}, "z", survivesIn("a", "b"), ""},
|
|
{"no other survives", []string{"a", "b", "c"}, "b", survivesIn("b"), ""},
|
|
{"empty order", nil, "a", survivesIn("a"), ""},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assert.Equal(t, tt.want, NearestSurvivingBranch(tt.order, tt.target, tt.survives))
|
|
})
|
|
}
|
|
}
|