Files

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: &note, 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)
}
}
}