This repository has been archived on 2026-10-25. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
submarine/internal/submodule/spine_test.go
T
simonfrankenandClaude Sonnet 5.5 4c1be3f9ab Show the pin, tip and checkout as a spine in the detail pane
Replace the Pinned/Checkout/Tip rows and the list of commits between pin
and tip with one line of history, newest on top: a node per commit with
its roles (pin, tip, you, staged, HEAD) and the number of commits between
two nodes. A staged pin shows the committed pin as its own node and notes
when it moves back. All three on one commit read "in sync".

Add Status.Drift, Committed and CommittedDrift and Status.Spine(); drop
Status.Between and Repo.FetchedAt. The fetch age moves from the header to
the detail pane (right-aligned in the name's line). The header summary is
right-aligned, ordered by importance and cut with an ellipsis if too narrow.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 16:34:14 +02:00

139 lines
4.2 KiB
Go

package submodule
import (
"slices"
"testing"
)
func TestSpine(t *testing.T) {
c := func(sha string) *Commit { return &Commit{SHA: sha} }
tests := []struct {
name string
st Status
nodes []string // "sha:roles"
links []Link // Below of every node but the last
}{
{
name: "in sync",
st: Status{Pinned: c("a"), CheckedOut: c("a"), Tip: c("a")},
nodes: []string{"a:pin,tip,you"},
},
{
name: "pin behind tip, checkout on pin",
st: Status{Pinned: c("p"), CheckedOut: c("p"), Tip: c("t"), Pin: PinInfo{Behind: 4}},
nodes: []string{"t:tip", "p:pin,you"},
links: []Link{{Above: 4}},
},
{
name: "pin on tip, checkout behind",
st: Status{Pinned: c("p"), CheckedOut: c("y"), Tip: c("p"), Drift: Distance{Behind: 2}},
nodes: []string{"p:pin,tip", "y:you"},
links: []Link{{Above: 2}},
},
{
name: "all apart",
st: Status{Pinned: c("p"), CheckedOut: c("y"), Tip: c("t"), Pin: PinInfo{Behind: 4}, Drift: Distance{Behind: 2}},
nodes: []string{"t:tip", "p:pin", "y:you"},
links: []Link{{Above: 4}, {Above: 2}},
},
{
name: "checkout ahead of the pin",
st: Status{Pinned: c("p"), CheckedOut: c("y"), Tip: c("t"), Pin: PinInfo{Behind: 5}, Drift: Distance{Ahead: 2}},
nodes: []string{"t:tip", "y:you", "p:pin"},
links: []Link{{Above: 3}, {Above: 2}},
},
{
name: "checkout on the tip",
st: Status{Pinned: c("p"), CheckedOut: c("t"), Tip: c("t"), Pin: PinInfo{Behind: 3}, Drift: Distance{Ahead: 3}},
nodes: []string{"t:tip,you", "p:pin"},
links: []Link{{Above: 3}},
},
{
name: "pin ahead of its ref",
st: Status{Pinned: c("p"), CheckedOut: c("p"), Tip: c("t"), Pin: PinInfo{Ahead: 1}},
nodes: []string{"p:pin,you", "t:tip"},
links: []Link{{Above: 1}},
},
{
name: "pin diverged from tip",
st: Status{Pinned: c("p"), CheckedOut: c("p"), Tip: c("t"), Pin: PinInfo{Ahead: 2, Behind: 3}},
nodes: []string{"t:tip", "p:pin,you"},
links: []Link{{Above: 3, Below: 2}},
},
{
name: "checkout diverged from pin",
st: Status{Pinned: c("p"), CheckedOut: c("y"), Tip: c("p"), Drift: Distance{Ahead: 1, Behind: 2}},
nodes: []string{"p:pin,tip", "y:you"},
links: []Link{{Above: 2, Below: 1}},
},
{
name: "staged pin above the committed one, checkout on the staged pin",
st: Status{Pinned: c("s"), CheckedOut: c("s"), Tip: c("s"), PinStaged: true,
Committed: c("p"), CommittedDrift: Distance{Behind: 3}},
nodes: []string{"s:staged,tip,you", "p:pin"},
links: []Link{{Above: 3}},
},
{
name: "checkout stayed on the committed pin",
st: Status{Pinned: c("s"), CheckedOut: c("p"), Tip: c("t"), PinStaged: true, Pin: PinInfo{Behind: 1},
Committed: c("p"), CommittedDrift: Distance{Behind: 2}, Drift: Distance{Behind: 2}},
nodes: []string{"t:tip", "s:staged", "p:pin,you"},
links: []Link{{Above: 1}, {Above: 2}},
},
{
name: "no tip, checkout not loaded",
st: Status{Pinned: c("p")},
nodes: []string{"p:pin"},
},
{
name: "superproject",
st: Status{Root: true, Pinned: c("h"), CheckedOut: c("h"), Tip: c("t"), Pin: PinInfo{Behind: 2}},
nodes: []string{"t:tip", "h:head"},
links: []Link{{Above: 2}},
},
}
words := map[Role]string{RolePin: "pin", RoleStaged: "staged", RoleHead: "head", RoleTip: "tip", RoleYou: "you"}
for _, tt := range tests {
var nodes []string
var links []Link
spine := tt.st.Spine()
for i, n := range spine {
s := n.Commit.SHA + ":"
for j, r := range n.Roles {
if j > 0 {
s += ","
}
s += words[r]
}
nodes = append(nodes, s)
if i < len(spine)-1 {
links = append(links, n.Below)
}
}
if !slices.Equal(nodes, tt.nodes) {
t.Errorf("%s: nodes %v, want %v", tt.name, nodes, tt.nodes)
}
if !slices.Equal(links, tt.links) {
t.Errorf("%s: links %v, want %v", tt.name, links, tt.links)
}
}
}
func TestSpineNodeInSync(t *testing.T) {
tests := []struct {
roles []Role
want bool
}{
{[]Role{RolePin, RoleTip, RoleYou}, true},
{[]Role{RoleHead, RoleTip}, true},
{[]Role{RolePin, RoleTip}, false},
{[]Role{RolePin, RoleYou}, false},
{[]Role{RoleTip}, false},
}
for _, tt := range tests {
if got := (SpineNode{Roles: tt.roles}).InSync(); got != tt.want {
t.Errorf("%v: InSync = %v, want %v", tt.roles, got, tt.want)
}
}
}