~bigbes/sr-ht-dolt

ref: 4520d5cf9385a3a7c18037e0c3dba95ad1874b35 sr-ht-dolt/web/views_test.go -rw-r--r-- 6.0 KiB
4520d5cf — Eugene Blikh feat(web): pluggable alternative-view registry for repositories 30 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package web

import (
	"context"
	"net/http"
	"net/url"
	"strings"
	"testing"

	"sourcecraft.dev/bigbes/sr-ht-dolt/browse"
	"sourcecraft.dev/bigbes/sr-ht-dolt/core"
)

// dummyView is a test-only View that fingerprints repos containing a table
// named "issues". It renders through an EXISTING page template ("404.html") so
// the test needs no newly embedded file. That template references only the
// shared chrome (basePage), so it renders cleanly with the fixed view envelope
// (which does not carry a .Tables field the way tree.html would demand). It
// records whether Build ran.
type dummyView struct {
	built bool
}

func (d *dummyView) Name() string     { return "issues" }
func (d *dummyView) Label() string    { return "Issues" }
func (d *dummyView) Template() string { return "404.html" }

func (d *dummyView) Applies(tables []browse.TableInfo) bool {
	for _, t := range tables {
		if t.Name == "issues" {
			return true
		}
	}
	return false
}

func (d *dummyView) Build(_ context.Context, sess BrowseSession, _ *core.Repo, ref string, _ url.Values) (any, error) {
	d.built = true
	// Pull data through the BrowseSession surface only, mirroring how a real
	// view works, and return the tables so tree.html renders something.
	tables, err := sess.Tables(context.Background(), ref)
	if err != nil {
		return nil, err
	}
	return tables, nil
}

func issueTables() []browse.TableInfo {
	return []browse.TableInfo{
		{Name: "issues", Columns: []browse.ColumnInfo{{Name: "id", PrimaryKey: true}}, RowCount: 2},
		{Name: "labels", RowCount: 5},
	}
}

func TestApplicableViewsFilters(t *testing.T) {
	v := &dummyView{}
	views := []View{v}

	got := applicableViews(views, issueTables())
	if len(got) != 1 || got[0] != v {
		t.Fatalf("applicableViews should select the matching view; got %v", got)
	}

	// No "issues" table → no applicable views.
	none := applicableViews(views, []browse.TableInfo{{Name: "widgets"}})
	if len(none) != 0 {
		t.Fatalf("applicableViews should filter out non-matching views; got %v", none)
	}
}

func TestApplicableViewsPreservesOrder(t *testing.T) {
	a := &dummyView{}
	// A second always-applies view to check registration order is preserved.
	b := alwaysView{}
	got := applicableViews([]View{b, a}, issueTables())
	if len(got) != 2 || got[0] != View(b) || got[1] != View(a) {
		t.Fatalf("applicableViews should preserve order; got %v", got)
	}
}

// alwaysView is a trivial always-applicable view used to check ordering.
type alwaysView struct{}

func (alwaysView) Name() string                    { return "always" }
func (alwaysView) Label() string                   { return "Always" }
func (alwaysView) Template() string                { return "404.html" }
func (alwaysView) Applies([]browse.TableInfo) bool { return true }
func (alwaysView) Build(context.Context, BrowseSession, *core.Repo, string, url.Values) (any, error) {
	return nil, nil
}

func TestHandleViewSelectedAndBuilt(t *testing.T) {
	h := newHarness(t)
	h.store.add(&core.Repo{Name: "db", OwnerID: 1, OwnerName: "alice", Path: "/d", Visibility: core.VisibilityPublic})
	h.browse.sess = &fakeSession{
		branches: []browse.Branch{{Name: "main", Head: "abcdef1234567890"}},
		tables:   issueTables(),
	}

	dv := &dummyView{}
	// Inject the view directly on the app, without polluting the global registry.
	setViews(t, h, dv)

	rec := h.do("GET", "/~alice/db/view/issues", nil, nil)
	// A 200 (rather than the 404 an unknown/non-applicable view returns) proves
	// the view was selected and its Template rendered; dv.built proves Build ran.
	if rec.Code != http.StatusOK {
		t.Fatalf("view page: got %d, want 200; body=%s", rec.Code, rec.Body.String())
	}
	if !dv.built {
		t.Fatalf("view.Build was not called")
	}
	// The view's Label flows into the page <title> via the envelope's basePage,
	// so its presence confirms this view (not a fallback) produced the response.
	if !strings.Contains(rec.Body.String(), "Issues") {
		t.Fatalf("view page missing view label in chrome; body=%s", rec.Body.String())
	}
}

func TestHandleViewUnknownSlugIs404(t *testing.T) {
	h := newHarness(t)
	h.store.add(&core.Repo{Name: "db", OwnerID: 1, OwnerName: "alice", Path: "/d", Visibility: core.VisibilityPublic})
	h.browse.sess = &fakeSession{
		branches: []browse.Branch{{Name: "main", Head: "abcdef1234567890"}},
		tables:   issueTables(),
	}
	setViews(t, h, &dummyView{})

	rec := h.do("GET", "/~alice/db/view/nope", nil, nil)
	if rec.Code != http.StatusNotFound {
		t.Fatalf("unknown view slug: got %d, want 404", rec.Code)
	}
}

func TestHandleViewNotApplicableIs404(t *testing.T) {
	h := newHarness(t)
	h.store.add(&core.Repo{Name: "db", OwnerID: 1, OwnerName: "alice", Path: "/d", Visibility: core.VisibilityPublic})
	// Tables lack "issues", so dummyView.Applies is false even though the slug matches.
	h.browse.sess = &fakeSession{
		branches: []browse.Branch{{Name: "main", Head: "abcdef1234567890"}},
		tables:   []browse.TableInfo{{Name: "widgets"}},
	}
	setViews(t, h, &dummyView{})

	rec := h.do("GET", "/~alice/db/view/issues", nil, nil)
	if rec.Code != http.StatusNotFound {
		t.Fatalf("non-applicable view: got %d, want 404", rec.Code)
	}
}

func TestOverviewShowsViewTabs(t *testing.T) {
	h := newHarness(t)
	h.store.add(&core.Repo{Name: "db", OwnerID: 1, OwnerName: "alice", Path: "/d", Visibility: core.VisibilityPublic})
	h.browse.sess = &fakeSession{
		branches: []browse.Branch{{Name: "main", Head: "abcdef1234567890"}},
		tables:   issueTables(),
	}
	setViews(t, h, &dummyView{})

	rec := h.do("GET", "/~alice/db", nil, nil)
	if rec.Code != http.StatusOK {
		t.Fatalf("overview: got %d, want 200", rec.Code)
	}
	body := rec.Body.String()
	if !strings.Contains(body, "/~alice/db/view/issues") {
		t.Fatalf("overview missing view tab link; body=%s", body)
	}
	if !strings.Contains(body, "nav-tabs") {
		t.Fatalf("overview missing tab bar; body=%s", body)
	}
}

// setViews overrides the app's view snapshot without touching the global
// registry, so view routing/overview tabs can be exercised with a controlled
// set. The harness retains the *app the router is mounted on.
func setViews(t *testing.T, h *harness, vs ...View) {
	t.Helper()
	h.app.views = vs
}