187 lines
7.2 KiB
Go
187 lines
7.2 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"testing"
|
|
"time"
|
|
|
|
"amnezia-share/internal/config"
|
|
"amnezia-share/internal/models"
|
|
"amnezia-share/internal/share"
|
|
"amnezia-share/internal/web"
|
|
)
|
|
|
|
// TestRenderAllPages executes every page template against a realistic view
|
|
// struct (as built by the real handlers) without needing a database. It
|
|
// catches execution-time template errors (bad field/method names, nil
|
|
// dereferences) that plain `go build` and template parsing cannot catch.
|
|
func TestRenderAllPages(t *testing.T) {
|
|
tmpl, err := web.LoadTemplates(config.Config{WebDir: "../../../web"})
|
|
if err != nil {
|
|
t.Fatalf("LoadTemplates: %v", err)
|
|
}
|
|
|
|
now := time.Now()
|
|
future := now.Add(48 * time.Hour)
|
|
note := "test note"
|
|
linkID := 7
|
|
email := "user@example.com"
|
|
validityDays := 30
|
|
|
|
serverInfo := models.ServerInfo{ID: 1, Label: "Germany", Protocols: []string{"wireguard", "awg2"}, Flag: "de", Speed: "1 Gbps"}
|
|
|
|
sampleLink := models.ShareLink{
|
|
ID: 1, Token: "abc123", MaxUses: 5, UseCount: 2, CreatedAt: now,
|
|
ExpiresAt: &future, ValidityDays: &validityDays,
|
|
AllowedServerIDs: json.RawMessage(`[1,2]`),
|
|
}
|
|
|
|
bundle := share.Bundle{
|
|
Base: "peer1",
|
|
Conf: &share.FilePart{Filename: "peer1.conf", Body: "conf-body", Mime: "text/plain"},
|
|
Vpn: &share.FilePart{Filename: "peer1.vpn", Body: "vpn-body", Mime: "text/plain"},
|
|
CreationID: 42,
|
|
CreatedAt: now,
|
|
Protocol: "wireguard",
|
|
ConnectionName: "peer1",
|
|
ServerID: 1,
|
|
}
|
|
|
|
adminUser := &models.User{ID: 1, Username: "admin"}
|
|
|
|
cases := []struct {
|
|
name string
|
|
data any
|
|
}{
|
|
{"page_install", installView{}},
|
|
{"page_install", installView{Installed: true}},
|
|
{"page_install", installView{Error: "test error"}},
|
|
{"page_login", loginView{PocketEnabled: true, NoAdmins: false}},
|
|
{"page_login", loginView{Error: "bad creds", NoAdmins: true}},
|
|
{"page_cabinet_auth", cabinetAuthView{}},
|
|
{"page_cabinet_auth", cabinetAuthView{Error: "bad creds"}},
|
|
{"page_cabinet", cabinetView{
|
|
Member: &models.Member{ID: 1, Username: "member1", Email: &email, CreatedAt: now},
|
|
Sub: &models.MemberSubscription{MemberID: 1, ExpiresAt: &future, MaxConfigs: 5, ConfigCount: 2},
|
|
SubLabel: "до " + future.Format("2006-01-02"),
|
|
ConfigsRemaining: 3,
|
|
Servers: []models.ServerInfo{serverInfo},
|
|
Bundles: []share.Bundle{bundle},
|
|
OK: "ok message",
|
|
}},
|
|
{"page_cabinet", cabinetView{
|
|
Member: &models.Member{ID: 1, Username: "member1", CreatedAt: now},
|
|
Error: "some error",
|
|
}},
|
|
{"page_share", shareView{Token: "abc123", NotFound: true}},
|
|
{"page_share", shareView{
|
|
Token: "abc123",
|
|
Link: &sampleLink,
|
|
Servers: []models.ServerInfo{serverInfo},
|
|
Bundles: []share.Bundle{bundle},
|
|
OK: "created",
|
|
}},
|
|
{"page_share", shareView{
|
|
Token: "abc123",
|
|
Link: &sampleLink,
|
|
Servers: []models.ServerInfo{serverInfo},
|
|
Expired: true,
|
|
LimitReached: true,
|
|
Error: "some error",
|
|
}},
|
|
{"page_faq", faqView{Items: []faqEntry{{Question: "Q1", Answer: "A1"}}}},
|
|
{"page_rules", nil},
|
|
{"page_status", statusView{Servers: []serverStatus{{ServerInfo: serverInfo, Alive: true, Ms: 42}, {ServerInfo: serverInfo, Alive: false, Err: "timeout"}}}},
|
|
{"page_admin_index", adminIndexView{
|
|
adminLayout: adminLayout{Admin: adminUser, Active: "index"},
|
|
TotalLinks: 10, ActiveLinks: 5, ExpiredLinks: 2, CleanedLinks: 1, LimitLinks: 2,
|
|
CodesCount: 3, PanelURL: "https://panel.example.com", PanelOK: true, Maintenance: false,
|
|
}},
|
|
{"page_admin_index", adminIndexView{
|
|
adminLayout: adminLayout{Admin: adminUser, Active: "index"},
|
|
PanelErr: "connection refused",
|
|
}},
|
|
{"page_admin_links", adminLinksView{
|
|
adminLayout: adminLayout{Admin: adminUser, Active: "links", OK: "saved"},
|
|
StatusFilter: "active",
|
|
Page: 1,
|
|
Total: 1,
|
|
PerPage: 20,
|
|
AllowedDurations: []int{7, 30, 90},
|
|
Links: []linkRow{
|
|
{ShareLink: sampleLink, Status: share.StatusActive, ActiveConfigs: 2},
|
|
},
|
|
Detail: &sampleLink,
|
|
DetailStatus: share.StatusActive,
|
|
Creations: []models.ShareCreation{{ID: 42, ShareLinkID: 1, ServerID: 1, Protocol: "wireguard", ConnectionName: "peer1", CreatedAt: now}},
|
|
DeletedCreations: []models.ShareCreation{{ID: 41, ShareLinkID: 1, ServerID: 1, Protocol: "wireguard", ConnectionName: "peer0", CreatedAt: now}},
|
|
AllowedServerIDs: []int{1, 2},
|
|
AllServers: []models.ServerInfo{serverInfo},
|
|
}},
|
|
{"page_admin_links", adminLinksView{
|
|
adminLayout: adminLayout{Admin: adminUser, Active: "links"},
|
|
PerPage: 20,
|
|
}},
|
|
{"page_admin_renewal", adminRenewalView{
|
|
adminLayout: adminLayout{Admin: adminUser, Active: "renewal"},
|
|
Codes: []models.RenewalCode{
|
|
{ID: 1, Code: "ABCD1234", AddDays: 30, MaxUses: 1, UseCount: 0, ShareLinkID: &linkID, CodeExpiresAt: &future, Note: ¬e, CodeTarget: "guest", CreatedAt: now},
|
|
{ID: 2, Code: "WXYZ9999", AddDays: 30, MaxUses: 5, UseCount: 1, CodeTarget: "member", CreatedAt: now},
|
|
},
|
|
}},
|
|
{"page_admin_renewal", adminRenewalView{adminLayout: adminLayout{Admin: adminUser, Active: "renewal"}}},
|
|
{"page_admin_configs", adminConfigsView{
|
|
adminLayout: adminLayout{Admin: adminUser, Active: "configs"},
|
|
Rows: []configRow{
|
|
{ShareCreation: models.ShareCreation{ID: 42, ShareLinkID: 1, ServerID: 1, Protocol: "wireguard", ConnectionName: "peer1", CreatedAt: now}, LinkID: 1, LinkToken: "abc123"},
|
|
},
|
|
LinkIDFilter: 1,
|
|
Truncated: true,
|
|
}},
|
|
{"page_admin_configs", adminConfigsView{adminLayout: adminLayout{Admin: adminUser, Active: "configs"}}},
|
|
{"page_admin_servers", adminServersView{
|
|
adminLayout: adminLayout{Admin: adminUser, Active: "servers"},
|
|
AllProtocols: share.GuestProtocols,
|
|
Rows: []serverRow{
|
|
{ID: 1, Title: "Germany", Protocols: []string{"wireguard", "awg2"}, Flag: "🇩🇪", Speed: "1 Gbps", Disabled: false, PanelName: "de-1", PanelHost: "1.2.3.4"},
|
|
{ID: 2, Title: "France", Protocols: nil, Disabled: true},
|
|
},
|
|
PanelReachable: true,
|
|
}},
|
|
{"page_admin_servers", adminServersView{
|
|
adminLayout: adminLayout{Admin: adminUser, Active: "servers"},
|
|
AllProtocols: share.GuestProtocols,
|
|
PanelErr: "connection refused",
|
|
}},
|
|
{"page_admin_settings", adminSettingsView{
|
|
adminLayout: adminLayout{Admin: adminUser, Active: "settings"},
|
|
PanelURL: "https://panel.example.com",
|
|
APIToken: "awp_secret",
|
|
ServerLabelsJSON: `{"1":"Germany"}`,
|
|
TrafficNotices: `{}`,
|
|
Maintenance: true,
|
|
PocketURL: "https://id.example.com",
|
|
PocketClientID: "client-id",
|
|
PocketSecret: "secret",
|
|
MemberDays: "30",
|
|
MemberMaxUses: "5",
|
|
TestResult: "Соединение установлено. Серверов обнаружено: 3.",
|
|
}},
|
|
{"page_admin_settings", adminSettingsView{
|
|
adminLayout: adminLayout{Admin: adminUser, Active: "settings"},
|
|
TestError: "connection refused",
|
|
}},
|
|
}
|
|
|
|
for i, c := range cases {
|
|
clone, err := tmpl.Clone()
|
|
if err != nil {
|
|
t.Fatalf("clone: %v", err)
|
|
}
|
|
if err := clone.ExecuteTemplate(io.Discard, c.name, c.data); err != nil {
|
|
t.Errorf("case %d (%s): execute failed: %v", i, c.name, err)
|
|
}
|
|
}
|
|
}
|