Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

Expand the admin status page into an operational dashboard

4506a4d3d279556435fdd414265e7499c3dada16 · Keenan Nemetz · 14d ago · parent 2e024cc9

server/status_test.go +143−4

@@ -1,23 +1,162 @@
1 1 package server_test
2 2
3 3 import (
4 "net/http"
4 5 "strings"
5 6 "testing"
7 "time"
8
9 "github.com/rodent-software/vole/forge/config"
10 "github.com/rodent-software/vole/forge/repo"
6 11 )
7 12
13 func (f *fixture) adminGet(t *testing.T, path string) (int, string) {
14 t.Helper()
15 if _, err := f.srv.Identity.SetAdmin(f.ownerID, true, "test"); err != nil {
16 t.Fatal(err)
17 }
18 _, cookie := f.browserSession(t, f.ownerID)
19 resp, body := f.browserGet(t, path, cookie)
20 return resp.StatusCode, body
21 }
22
8 23 func TestStatusPageSummarizesTheInstance(t *testing.T) {
9 24 t.Parallel()
10 25 f := newFixture(t)
11 if code, body := f.get("/status"); code != 200 || !strings.Contains(body, "All core services are responding") {
26 code, body := f.adminGet(t, "/status")
27 if code != 200 {
12 28 t.Fatalf("status page = %d:\n%s", code, body)
13 29 }
14 _, body := f.get("/status")
15 for _, want := range []string{"Accounts", "Repositories", "Queued runs", "Runners", "identity", "queue"} {
30 for _, want := range []string{"Overview", "Storage", "Runners", "Configuration", "Storage used", "Free space", "LFS objects", "Page views", "Unique users", "Today (UTC)"} {
16 31 if !strings.Contains(body, want) {
17 t.Fatalf("status page does not show %q:\n%s", want, body)
32 t.Fatalf("status overview does not show %q:\n%s", want, body)
33 }
34 }
35 for _, gone := range []string{"Service health", "All core services are responding", "identity", "Runners and queue", "Instance and access"} {
36 if strings.Contains(body, gone) {
37 t.Fatalf("status overview shows content from another tab or removed health text %q:\n%s", gone, body)
18 38 }
19 39 }
20 40 if strings.Contains(body, f.repo) {
21 41 t.Fatalf("status page named a repository:\n%s", body)
22 42 }
23 43 }
44
45 func TestStatusPageShowsConfigurationToAdmins(t *testing.T) {
46 t.Parallel()
47 f := newFixtureWith(t, func(c *config.Config) {
48 c.InstanceName = "Acme Forge"
49 c.PagesDomain = "pages.example.com"
50 c.MaxBlobBytes = 1234
51 })
52
53 // The page is instance-admin only: anonymous goes to login, and a signed-in
54 // non-admin gets the same 404 as a missing page.
55 if resp, _ := f.browserGet(t, "/status", ""); resp.StatusCode != http.StatusSeeOther || !strings.Contains(resp.Header.Get("Location"), "/login") {
56 t.Fatalf("anonymous status page = %d %q", resp.StatusCode, resp.Header.Get("Location"))
57 }
58 f.addWriter(t)
59 _, writerCookie := f.browserSession(t, f.writerID)
60 if resp, _ := f.browserGet(t, "/status?tab=configuration", writerCookie); resp.StatusCode != http.StatusNotFound {
61 t.Fatalf("non-admin status page = %d, want 404", resp.StatusCode)
62 }
63
64 if _, err := f.srv.Identity.SetAdmin(f.ownerID, true, "test"); err != nil {
65 t.Fatal(err)
66 }
67 _, cookie := f.browserSession(t, f.ownerID)
68 resp, body := f.browserGet(t, "/status?tab=configuration", cookie)
69 if resp.StatusCode != 200 {
70 t.Fatalf("admin status page = %d:\n%s", resp.StatusCode, body)
71 }
72 for _, want := range []string{
73 "Configuration", "Instance and access", "Acme Forge", "pages.example.com",
74 "max_blob_bytes", "1.2 KiB (1234 bytes)", "built-in defaults",
75 } {
76 if !strings.Contains(body, want) {
77 t.Fatalf("admin status page does not show %q:\n%s", want, body)
78 }
79 }
80 }
81
82 func TestStatusPageCountsPageViews(t *testing.T) {
83 t.Parallel()
84 f := newFixture(t)
85
86 for i := 0; i < 3; i++ {
87 if code, _ := f.get("/"); code != 200 {
88 t.Fatalf("anonymous page %d = %d", i, code)
89 }
90 }
91 _, cookie := f.browserSession(t, f.ownerID)
92 if resp, _ := f.browserGet(t, "/", cookie); resp.StatusCode != 200 {
93 t.Fatalf("signed-in page = %d", resp.StatusCode)
94 }
95
96 stats, err := f.srv.Web.PageViews.Stats(time.Now().UTC())
97 if err != nil {
98 t.Fatal(err)
99 }
100 if stats.Today.Views != 4 || stats.Today.Users != 1 {
101 t.Fatalf("today = %+v, want 4 views and 1 signed-in user", stats.Today)
102 }
103
104 // The status page must not count itself, or a monitor turns the metric into
105 // a measure of polling.
106 if code, _ := f.adminGet(t, "/status"); code != 200 {
107 t.Fatalf("status = %d", code)
108 }
109 stats, err = f.srv.Web.PageViews.Stats(time.Now().UTC())
110 if err != nil {
111 t.Fatal(err)
112 }
113 if stats.Today.Views != 4 {
114 t.Fatalf("status page changed the view count to %d", stats.Today.Views)
115 }
116 }
117
118 func TestStatusPageTabsSplitTheSections(t *testing.T) {
119 t.Parallel()
120 f := newFixture(t)
121 if _, err := f.srv.Repos.Create("private", f.ownerID, "private example"); err != nil {
122 t.Fatal(err)
123 }
124 if err := f.srv.Index.Upsert(mustRepoNamed(t, f, "private")); err != nil {
125 t.Fatal(err)
126 }
127
128 code, storage := f.adminGet(t, "/status?tab=storage")
129 if code != 200 || !strings.Contains(storage, "Repositories") || !strings.Contains(storage, "LFS and build objects") {
130 t.Fatalf("storage tab = %d:\n%s", code, storage)
131 }
132 for _, gone := range []string{"Page views", "Runners and queue", "Instance and access"} {
133 if strings.Contains(storage, gone) {
134 t.Fatalf("storage tab shows %q:", gone)
135 }
136 }
137
138 code, runners := f.adminGet(t, "/status?tab=runners")
139 if code != 200 || !strings.Contains(runners, "Runners and queue") || !strings.Contains(runners, "Queue") {
140 t.Fatalf("runners tab = %d:\n%s", code, runners)
141 }
142 if strings.Contains(runners, "Storage used") || strings.Contains(runners, "Page views") {
143 t.Fatalf("runners tab shows overview content:\n%s", runners)
144 }
145
146 code, config := f.adminGet(t, "/status?tab=configuration")
147 if code != 200 || !strings.Contains(config, "Configuration") || !strings.Contains(config, "Instance and access") {
148 t.Fatalf("configuration tab = %d:\n%s", code, config)
149 }
150 if strings.Contains(config, "Storage used") || strings.Contains(config, "Runners and queue") {
151 t.Fatalf("configuration tab shows another tab's content:\n%s", config)
152 }
153 }
154
155 func mustRepoNamed(t *testing.T, f *fixture, name string) *repo.Meta {
156 t.Helper()
157 m, err := f.srv.Repos.Open(name)
158 if err != nil {
159 t.Fatal(err)
160 }
161 return m
162 }