internal/handlers/templates_test.go
4.4 KB
| 1 | package handlers |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "net/http/httptest" |
| 6 | "testing" |
| 7 | "time" |
| 8 | |
| 9 | "github.com/clancore/repo/internal/models" |
| 10 | "github.com/clancore/repo/internal/services" |
| 11 | ) |
| 12 | |
| 13 | func TestTemplatesRender(t *testing.T) { |
| 14 | tmpl := NewTemplates() |
| 15 | if err := tmpl.LoadDir("../../templates"); err != nil { |
| 16 | t.Fatalf("load templates: %v", err) |
| 17 | } |
| 18 | |
| 19 | now := time.Date(2026, 1, 2, 15, 4, 5, 0, time.UTC) |
| 20 | user := &models.User{ |
| 21 | ID: 1, |
| 22 | Username: "testuser", |
| 23 | Email: "test@example.com", |
| 24 | CreatedAt: now, |
| 25 | } |
| 26 | repo := &models.Repo{ |
| 27 | ID: 1, |
| 28 | Name: "demo", |
| 29 | OwnerID: user.ID, |
| 30 | Owner: user, |
| 31 | CloneURL: "testuser/demo", |
| 32 | Description: "Демонстрационный репозиторий", |
| 33 | CreatedAt: now, |
| 34 | UpdatedAt: now, |
| 35 | } |
| 36 | finished := now.Add(time.Minute) |
| 37 | build := &models.Build{ |
| 38 | ID: 1, |
| 39 | RepoID: repo.ID, |
| 40 | Branch: "main", |
| 41 | BuildType: "go", |
| 42 | Status: "success", |
| 43 | StartedAt: now, |
| 44 | FinishedAt: &finished, |
| 45 | CreatedAt: now, |
| 46 | } |
| 47 | release := &models.Release{ |
| 48 | ID: 1, |
| 49 | RepoID: repo.ID, |
| 50 | Tag: "v1.0.0", |
| 51 | Title: "Первый релиз", |
| 52 | Body: "Что нового\n- первая версия", |
| 53 | IsPrerelease: false, |
| 54 | CreatedAt: now, |
| 55 | } |
| 56 | releases := []*models.Release{release} |
| 57 | commits := []services.CommitInfo{{ |
| 58 | Hash: "5030ca0a", |
| 59 | Author: "testuser", |
| 60 | Time: now, |
| 61 | Subject: "Initial commit", |
| 62 | }} |
| 63 | tree := []services.FileEntry{ |
| 64 | {Name: "main.go", Path: "main.go", Type: "blob", Size: 1024, |
| 65 | Commit: &services.CommitInfo{Hash: "5030ca0a", Author: "testuser", Time: now, Subject: "Initial commit"}}, |
| 66 | {Name: "src", Path: "src", Type: "tree"}, |
| 67 | } |
| 68 | repos := []*models.Repo{repo} |
| 69 | rows := []struct { |
| 70 | B *models.Build |
| 71 | Repo *models.Repo |
| 72 | }{{B: build, Repo: repo}} |
| 73 | |
| 74 | base := map[string]interface{}{ |
| 75 | "UserID": user.ID, |
| 76 | "User": user, |
| 77 | "CSRF": "test-token", |
| 78 | "Query": "", |
| 79 | "BaseURL": "http://localhost:5758", |
| 80 | } |
| 81 | page := func(extra map[string]interface{}) map[string]interface{} { |
| 82 | d := make(map[string]interface{}, len(base)+len(extra)) |
| 83 | for k, v := range base { |
| 84 | d[k] = v |
| 85 | } |
| 86 | for k, v := range extra { |
| 87 | d[k] = v |
| 88 | } |
| 89 | return d |
| 90 | } |
| 91 | |
| 92 | pages := map[string]map[string]interface{}{ |
| 93 | "home": page(map[string]interface{}{ |
| 94 | "PublicRepos": repos, |
| 95 | }), |
| 96 | "repo": page(map[string]interface{}{ |
| 97 | "Repo": repo, |
| 98 | "IsOwner": true, |
| 99 | "Branches": []string{"main", "dev"}, |
| 100 | "ActiveBranch": "main", |
| 101 | "Commits": commits, |
| 102 | "Tree": tree, |
| 103 | "Path": "", |
| 104 | "Crumbs": []Crumb(nil), |
| 105 | "TreeError": false, |
| 106 | "BranchCount": 2, |
| 107 | "TagCount": 1, |
| 108 | "CommitCount": 7, |
| 109 | "Releases": releases, |
| 110 | "ReleaseCount": 1, |
| 111 | }), |
| 112 | "file": page(map[string]interface{}{ |
| 113 | "Repo": repo, |
| 114 | "Path": "main.go", |
| 115 | "Crumbs": []Crumb{{Name: "main.go", Path: "main.go"}}, |
| 116 | "Branch": "main", |
| 117 | "Content": "package main\n\nfunc main() {}\n", |
| 118 | "FileSize": "27 B", |
| 119 | "LangClass": langClass("main.go"), |
| 120 | }), |
| 121 | "dashboard": page(map[string]interface{}{ |
| 122 | "Repos": repos, |
| 123 | }), |
| 124 | "builds": page(map[string]interface{}{ |
| 125 | "Rows": rows, |
| 126 | "FilterRepo": repo, |
| 127 | }), |
| 128 | "build": page(map[string]interface{}{ |
| 129 | "Repo": repo, |
| 130 | "Build": build, |
| 131 | "BuildID": build.ID, |
| 132 | "BuildLog": "=== build started ===\ntype: go\n=== build SUCCESS ===\n", |
| 133 | }), |
| 134 | "login": page(map[string]interface{}{ |
| 135 | "Error": "", |
| 136 | }), |
| 137 | "register": page(map[string]interface{}{ |
| 138 | "Error": "", |
| 139 | }), |
| 140 | "repo-create": page(map[string]interface{}{ |
| 141 | "Error": "", |
| 142 | }), |
| 143 | "profile": page(map[string]interface{}{ |
| 144 | "ProfileUser": user, |
| 145 | "Repos": repos, |
| 146 | }), |
| 147 | "releases": page(map[string]interface{}{ |
| 148 | "Repo": repo, |
| 149 | "IsOwner": true, |
| 150 | "Releases": releases, |
| 151 | }), |
| 152 | "release-new": page(map[string]interface{}{ |
| 153 | "Repo": repo, |
| 154 | "Error": "", |
| 155 | "Tag": "v1.0.0", |
| 156 | "Title": "Первый релиз", |
| 157 | "Body": "Что нового", |
| 158 | "IsPrerelease": false, |
| 159 | }), |
| 160 | } |
| 161 | |
| 162 | for name, data := range pages { |
| 163 | t.Run(name, func(t *testing.T) { |
| 164 | rec := httptest.NewRecorder() |
| 165 | tmpl.Render(rec, name, data) |
| 166 | if rec.Code != 200 { |
| 167 | t.Fatalf("status = %d, body = %s", rec.Code, rec.Body.String()) |
| 168 | } |
| 169 | body := rec.Body.Bytes() |
| 170 | if !bytes.Contains(body, []byte("</html>")) { |
| 171 | t.Fatalf("page %s was not fully rendered", name) |
| 172 | } |
| 173 | if bytes.Contains(body, []byte("<no value>")) { |
| 174 | t.Fatalf("page %s contains missing template values", name) |
| 175 | } |
| 176 | }) |
| 177 | } |
| 178 | } |