summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Fischer <martin@push-f.com>2025-04-06 14:56:19 +0200
committerMartin Fischer <martin@push-f.com>2025-04-13 20:19:45 +0200
commitd218bee6b8ab4fd10bec88269270a2412bdbbb46 (patch)
treeb3c9bd74135f8531bbb6be2d53b1eb9acb1386b0
parent056e15fb6ac33b749b78f83e410e57a9d64aedea (diff)
refactor: make domain configurable
-rw-r--r--go.mod3
-rw-r--r--go.sum2
-rw-r--r--lexsurf.go19
-rw-r--r--lexsurf_test.go144
4 files changed, 161 insertions, 7 deletions
diff --git a/go.mod b/go.mod
index fe9bf18..e2f8382 100644
--- a/go.mod
+++ b/go.mod
@@ -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
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..9d6ce76
--- /dev/null
+++ b/go.sum
@@ -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=
diff --git a/lexsurf.go b/lexsurf.go
index 9ac8feb..5c1e15a 100644
--- a/lexsurf.go
+++ b/lexsurf.go
@@ -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&nbsp;national&nbsp;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)
+ }
+}