Fold the unpublished projects into the overview table
They were a list underneath the table, which meant reading the same project names in two different shapes. They are rows now, with the action in an "init" column that only appears when some row needs it, and they sort to the bottom as their own group: an un-inited directory is a different kind of task and should not push the daily ones down. Every directory under the base gets a row, not just the repositories -- `init` is exactly what turns a plain directory into a project, so leaving those out would have hidden the ones the column is for. Such a row has no git state to show and costs no subprocesses either, since projectStatus now checks for .git before running any. The count line gained "N to init"; the projects count still counts repositories, so the two numbers stay meaningful side by side. An unreachable server marks nothing at all, as before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+51
-30
@@ -82,42 +82,63 @@ func TestCommitHostRe(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestReportUnpublishedUnreachableServer: when the server cannot be listed, the
|
||||
// overview must say so rather than claim every project is missing there.
|
||||
func TestReportUnpublishedUnreachableServer(t *testing.T) {
|
||||
out := captureStdout(t, func() {
|
||||
reportUnpublished([]string{"a", "b"}, nil, errors.New("network is unreachable"))
|
||||
})
|
||||
if strings.Contains(out, "not on the server") {
|
||||
t.Errorf("an unreachable server was reported as missing repositories: %q", out)
|
||||
}
|
||||
if !strings.Contains(out, "not reachable") {
|
||||
t.Errorf("no hint that the server was unreachable: %q", out)
|
||||
// TestMarkUnpublishedUnreachableServer: a listing that failed must leave every
|
||||
// row unmarked. Not knowing is not the same as knowing they are missing —
|
||||
// marking all of them would tell the user to re-init their whole base.
|
||||
func TestMarkUnpublishedUnreachableServer(t *testing.T) {
|
||||
rows := []projStatus{{name: "a"}, {name: "b"}}
|
||||
markUnpublished(rows, nil, errors.New("network is unreachable"))
|
||||
for _, r := range rows {
|
||||
if r.notOnServer {
|
||||
t.Errorf("%s marked as missing although the server could not be listed", r.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestReportUnpublished names only the local projects the server has never
|
||||
// seen. The other direction — repositories there but not here — is what `list`
|
||||
// is for, and repeating it here only added noise.
|
||||
func TestReportUnpublished(t *testing.T) {
|
||||
out := captureStdout(t, func() {
|
||||
reportUnpublished([]string{"both", "onlyhere"}, []string{"both", "onlythere"}, nil)
|
||||
})
|
||||
if !strings.Contains(out, "not on the server (init)") || !strings.Contains(out, "onlyhere") {
|
||||
t.Errorf("local-only project not reported: %q", out)
|
||||
// TestMarkUnpublished flags only what the server really does not have.
|
||||
func TestMarkUnpublished(t *testing.T) {
|
||||
rows := []projStatus{{name: "both"}, {name: "onlyhere"}}
|
||||
markUnpublished(rows, []string{"both", "onlythere"}, nil)
|
||||
if rows[0].notOnServer {
|
||||
t.Error("a project present on both sides was marked")
|
||||
}
|
||||
if strings.Contains(out, "onlythere") {
|
||||
t.Errorf("server-only project should no longer be listed: %q", out)
|
||||
}
|
||||
if strings.Contains(out, "both") {
|
||||
t.Errorf("a project present on both sides should not be listed: %q", out)
|
||||
if !rows[1].notOnServer {
|
||||
t.Error("a local-only project was not marked")
|
||||
}
|
||||
}
|
||||
|
||||
// nothing unpublished: no line at all, not an empty label
|
||||
if out := captureStdout(t, func() {
|
||||
reportUnpublished([]string{"both"}, []string{"both"}, nil)
|
||||
}); strings.TrimSpace(out) != "" {
|
||||
t.Errorf("nothing to report should print nothing, got %q", out)
|
||||
// TestUnpublishedRowsCarryTheHint: the entries live in the table now, with the
|
||||
// action in their own column, rather than in a list underneath it.
|
||||
func TestUnpublishedRowsCarryTheHint(t *testing.T) {
|
||||
useColor = false
|
||||
rows := []projStatus{
|
||||
{name: "published", isRepo: true, hasUpstream: true},
|
||||
{name: "fresh", isRepo: true, notOnServer: true},
|
||||
{name: "notarepo", notOnServer: true},
|
||||
}
|
||||
w := measureOverview(rows)
|
||||
if !w.hint {
|
||||
t.Fatal("hint column not reserved although rows need it")
|
||||
}
|
||||
got := []string{}
|
||||
for _, r := range rows {
|
||||
got = append(got, formatProjStatus(r, w))
|
||||
}
|
||||
if strings.Contains(got[0], "init") {
|
||||
t.Errorf("a published project was hinted: %q", got[0])
|
||||
}
|
||||
for _, i := range []int{1, 2} {
|
||||
if !strings.Contains(got[i], "init") {
|
||||
t.Errorf("row %d missing the init hint: %q", i, got[i])
|
||||
}
|
||||
}
|
||||
// a directory that is not a repository has no sync state to report
|
||||
if strings.ContainsAny(got[2], "✓–↑↓") {
|
||||
t.Errorf("non-repository row claims a git state: %q", got[2])
|
||||
}
|
||||
// with nothing to hint the column disappears entirely
|
||||
if measureOverview(rows[:1]).hint {
|
||||
t.Error("hint column reserved although no row needs it")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user