diff options
author | Martin Fischer <martin@push-f.com> | 2025-04-06 14:56:19 +0200 |
---|---|---|
committer | Martin Fischer <martin@push-f.com> | 2025-04-13 20:19:45 +0200 |
commit | d218bee6b8ab4fd10bec88269270a2412bdbbb46 (patch) | |
tree | b3c9bd74135f8531bbb6be2d53b1eb9acb1386b0 | |
parent | 056e15fb6ac33b749b78f83e410e57a9d64aedea (diff) |
refactor: make domain configurable
-rw-r--r-- | go.mod | 3 | ||||
-rw-r--r-- | go.sum | 2 | ||||
-rw-r--r-- | lexsurf.go | 19 | ||||
-rw-r--r-- | lexsurf_test.go | 144 |
4 files changed, 161 insertions, 7 deletions
@@ -1,3 +1,6 @@ module push-f.com/lex-surf go 1.23.5 + +// test dependencies +require github.com/peter-evans/patience v0.3.0 @@ -0,0 +1,2 @@ +github.com/peter-evans/patience v0.3.0 h1:rX0JdJeepqdQl1Sk9c9uvorjYYzL2TfgLX1adqYm9cA= +github.com/peter-evans/patience v0.3.0/go.mod h1:Kmxu5sY1NmBLFSStvXjX1wS9mIv7wMcP/ubucyMOAu0= @@ -6,12 +6,18 @@ import ( "log" "net/http" "net/url" + "os" "strings" "text/template" ) func main() { - var handler = handler{lawsByCC: map[string]map[string]law{}} + domain := os.Getenv("DOMAIN") + if domain == "" { + log.Fatal("DOMAIN environment variable must be set") + } + + var handler = handler{domain: domain, lawsByCC: map[string]map[string]law{}} text, _ := ioutil.ReadFile("countries.json") err := json.Unmarshal(text, &handler.countries) if err != nil { @@ -50,12 +56,13 @@ var tpl, _ = template.New("").Funcs(template.FuncMap{ }).ParseGlob("views/*") type handler struct { + domain string countries map[string]country lawsByCC map[string]map[string]law } func (h *handler) handle(w http.ResponseWriter, r *http.Request) { - if r.Host == "lex.surf" || r.Host == "lex.localhost" { + if r.Host == h.domain { if r.URL.Path != "/" { w.WriteHeader(http.StatusNotFound) w.Write([]byte("page not found")) @@ -71,13 +78,11 @@ func (h *handler) handle(w http.ResponseWriter, r *http.Request) { return } - parts := strings.SplitN(r.Host, ".", 2) - if len(parts) != 2 { + cc, isSubdomain := strings.CutSuffix(r.Host, "."+h.domain) + if !isSubdomain { w.Write([]byte("unknown host")) return } - cc := parts[0] - host := parts[1] key := strings.TrimLeft(r.URL.Path, "/") if len(key) > 0 { val, ok := h.lawsByCC[cc][key] @@ -106,7 +111,7 @@ func (h *handler) handle(w http.ResponseWriter, r *http.Request) { _, hasJSONLaws := h.lawsByCC[cc] err := tpl.ExecuteTemplate(w, "search.html", map[string]any{ "TLD": cc, - "Domain": host, + "Domain": h.domain, "Country": h.countries[cc], "HasJSONLaws": hasJSONLaws, }) diff --git a/lexsurf_test.go b/lexsurf_test.go new file mode 100644 index 0000000..e647a2f --- /dev/null +++ b/lexsurf_test.go @@ -0,0 +1,144 @@ +package main + +import ( + "net/http/httptest" + "strings" + "testing" + + "github.com/peter-evans/patience" +) + +func newHandler() handler { + return handler{ + domain: "lex.example", + countries: map[string]country{ + "zu": country{ + Name: "Zubrowka", + SearchURL: "https://lex.gov.zu/?search=%s", + }, + }, + lawsByCC: map[string]map[string]law{}, + } +} + +func TestStartPage(t *testing.T) { + h := newHandler() + + req := httptest.NewRequest("GET", "/", nil) + req.Host = "lex.example" + w := httptest.NewRecorder() + h.handle(w, req) + + resp := w.Result() + if resp.StatusCode != 200 { + t.Errorf("expected status 200 OK, got %d", resp.StatusCode) + } + expected := `<!doctype html> +<html> +<head> + <title>Lex.surf: Portal to National Law</title> + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> + <link rel="stylesheet" href="/assets/style.css" /> +</head> +<body> + <img alt="lex.surf" height=140 class=logo src="/assets/logo.svg"> + <h2>The portal to national law.</h2> + <div class=countries> + <a class=cc-link href="//zu.lex.example" title="Zubrowka">ZU</a> + </div> +</body> +</html> +` + assertEqual(t, w.Body.String(), expected) +} + +func TestSearch(t *testing.T) { + h := newHandler() + + req := httptest.NewRequest("GET", "/", nil) + req.Host = "zu.lex.example" + w := httptest.NewRecorder() + h.handle(w, req) + + resp := w.Result() + if resp.StatusCode != 200 { + t.Errorf("expected status 200 OK, got %d", resp.StatusCode) + } + expected := `<!doctype html> +<html> +<head> + <title>National Law of Zubrowka</title> + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> + <link rel="stylesheet" href="//lex.example/assets/style.css" /> +</head> +<body> + <a href="//lex.example"><img alt="lex.surf" height=140 class=logo src="//lex.example/assets/logo.svg"></a> + <h1>National Law of Zubrowka</h1> + + + <form> + + + <input id=search name=q aria-label="Search" autocomplete="off" autofocus > + <ul id=suggestions></ul> + + + </form> + + <script src="//lex.example/assets/script.js"></script> +</body> +</html> +` + assertEqual(t, w.Body.String(), expected) +} + +func TestSearchRedirect(t *testing.T) { + h := newHandler() + + req := httptest.NewRequest("GET", "/?q=tourism", nil) + req.Host = "zu.lex.example" + w := httptest.NewRecorder() + h.handle(w, req) + + resp := w.Result() + if resp.StatusCode != 302 { + t.Errorf("expected status 302, got %d", resp.StatusCode) + } + + if resp.Header.Get("Location") != "https://lex.gov.zu/?search=tourism" { + t.Errorf("wrong location, got %s", resp.Header.Get("Location")) + } +} + +func TestLawRedirect(t *testing.T) { + h := newHandler() + + h.lawsByCC["zu"] = map[string]law{} + h.lawsByCC["zu"]["zepl"] = law{ + URL: "https://lex.gov.zu/zeppelin-code", + } + + req := httptest.NewRequest("GET", "/zepl", nil) + req.Host = "zu.lex.example" + w := httptest.NewRecorder() + h.handle(w, req) + + resp := w.Result() + if resp.StatusCode != 302 { + t.Errorf("expected status 302, got %d", resp.StatusCode) + } + + if resp.Header.Get("Location") != "https://lex.gov.zu/zeppelin-code" { + t.Errorf("wrong location, got %s", resp.Header.Get("Location")) + } +} + +func assertEqual(t *testing.T, received string, expected string) { + if received != expected { + a := strings.Split(received, "\n") + b := strings.Split(expected, "\n") + diffs := patience.Diff(a, b) + unidiff := patience.UnifiedDiffText(diffs) + t.Error(unidiff) + } +} |