test: add unit tests
This commit is contained in:
parent
c8747c0182
commit
afc0347a1b
18
.github/workflows/test.yml
vendored
Normal file
18
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
name: Test and Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- name: Install dependencies
|
||||
run: go get .
|
||||
- name: Build
|
||||
run: make build
|
||||
- name: Test
|
||||
run: make test
|
2
Makefile
2
Makefile
@ -8,5 +8,7 @@ build:gentempl esbuild
|
||||
templ generate
|
||||
cat ./style/*.css | ./esbuild --loader=css --minify > ./static/style.css
|
||||
go build -ldflags="-X 'github.com/rramiachraf/dumb/data.Version=$(VERSION)' -s -w"
|
||||
test:
|
||||
go test ./... -v
|
||||
fmt:
|
||||
templ fmt .
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Album(l *logrus.Logger) http.HandlerFunc {
|
||||
func album(l *logrus.Logger) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
artist := mux.Vars(r)["artist"]
|
||||
albumName := mux.Vars(r)["albumName"]
|
||||
|
39
handlers/album_test.go
Normal file
39
handlers/album_test.go
Normal file
@ -0,0 +1,39 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func TestAlbum(t *testing.T) {
|
||||
url := "/albums/Daft-punk/Random-access-memories"
|
||||
title := "Give Life Back to Music"
|
||||
|
||||
r, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
l := logrus.New()
|
||||
m := New(l)
|
||||
|
||||
m.ServeHTTP(rr, r)
|
||||
|
||||
defer rr.Result().Body.Close()
|
||||
|
||||
doc, err := goquery.NewDocumentFromReader(rr.Result().Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
docTitle := doc.Find("#album-tracklist > a > p").First().Text()
|
||||
|
||||
if docTitle != title {
|
||||
t.Fatalf("expected %q, got %q\n", title, docTitle)
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Annotations(l *logrus.Logger) http.HandlerFunc {
|
||||
func annotations(l *logrus.Logger) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := mux.Vars(r)["id"]
|
||||
if a, err := getCache[data.Annotation]("annotation:" + id); err == nil {
|
||||
|
38
handlers/annotations_test.go
Normal file
38
handlers/annotations_test.go
Normal file
@ -0,0 +1,38 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func TestAnnotations(t *testing.T) {
|
||||
url := "/61590/Black-star-respiration/The-new-moon-rode-high-in-the-crown-of-the-metropolis/annotations"
|
||||
|
||||
r, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
l := logrus.New()
|
||||
m := New(l)
|
||||
|
||||
m.ServeHTTP(rr, r)
|
||||
|
||||
defer rr.Result().Body.Close()
|
||||
|
||||
decoder := json.NewDecoder(rr.Result().Body)
|
||||
annotation := map[string]string{}
|
||||
|
||||
if err := decoder.Decode(&annotation); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, exists := annotation["html"]; !exists {
|
||||
t.Fatalf("html field not found on annotation\n")
|
||||
}
|
||||
}
|
25
handlers/cache_test.go
Normal file
25
handlers/cache_test.go
Normal file
@ -0,0 +1,25 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
key := "testkey"
|
||||
value := []byte("testvalue")
|
||||
|
||||
err := setCache(key, value)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to set cache, %q\n", err)
|
||||
}
|
||||
|
||||
v, err := getCache[[]byte](key)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get cache, %q\n", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(v, value) {
|
||||
t.Fatalf("expected %q, got %q\n", value, v)
|
||||
}
|
||||
}
|
32
handlers/handler.go
Normal file
32
handlers/handler.go
Normal file
@ -0,0 +1,32 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/rramiachraf/dumb/views"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func New(logger *logrus.Logger) *mux.Router {
|
||||
r := mux.NewRouter()
|
||||
|
||||
r.Use(mustHeaders)
|
||||
|
||||
r.Handle("/", templ.Handler(views.HomePage()))
|
||||
r.HandleFunc("/{id}-lyrics", lyrics(logger)).Methods("GET")
|
||||
r.HandleFunc("/albums/{artist}/{albumName}", album(logger)).Methods("GET")
|
||||
r.HandleFunc("/images/{filename}.{ext}", imageProxy(logger)).Methods("GET")
|
||||
r.HandleFunc("/search", search(logger)).Methods("GET")
|
||||
r.HandleFunc("/{id}/{artist-song}/{verse}/annotations", annotations(logger)).Methods("GET")
|
||||
r.HandleFunc("/instances.json", instances(logger)).Methods("GET")
|
||||
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
||||
r.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
views.ErrorPage(404, "page not found").Render(context.Background(), w)
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
@ -20,7 +20,7 @@ func sendError(err error, status int, msg string, l *logrus.Logger, w http.Respo
|
||||
}
|
||||
}
|
||||
|
||||
func Instances(l *logrus.Logger) http.HandlerFunc {
|
||||
func instances(l *logrus.Logger) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if instances, err := getCache[[]byte]("instances"); err == nil {
|
||||
w.Header().Set("content-type", ContentTypeJSON)
|
||||
|
40
handlers/instances_test.go
Normal file
40
handlers/instances_test.go
Normal file
@ -0,0 +1,40 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func TestInstancesList(t *testing.T) {
|
||||
r, err := http.NewRequest(http.MethodGet, "/instances.json", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
l := logrus.New()
|
||||
|
||||
m := New(l)
|
||||
m.ServeHTTP(rr, r)
|
||||
|
||||
c := rr.Result().Header.Get("content-type")
|
||||
if c != ContentTypeJSON {
|
||||
t.Fatalf("expected %q, got %q", ContentTypeJSON, c)
|
||||
}
|
||||
|
||||
defer rr.Result().Body.Close()
|
||||
|
||||
d := json.NewDecoder(rr.Result().Body)
|
||||
instances := []map[string]any{}
|
||||
if err := d.Decode(&instances); err != nil {
|
||||
t.Fatalf("unable to decode json from response, %q\n", err)
|
||||
}
|
||||
|
||||
if _, exists := instances[0]["clearnet"]; !exists {
|
||||
t.Fatal("unable to get clearnet value from instances list")
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Lyrics(l *logrus.Logger) http.HandlerFunc {
|
||||
func lyrics(l *logrus.Logger) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := mux.Vars(r)["id"]
|
||||
|
||||
|
45
handlers/lyrics_test.go
Normal file
45
handlers/lyrics_test.go
Normal file
@ -0,0 +1,45 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func TestLyrics(t *testing.T) {
|
||||
url := "/The-silver-seas-catch-yer-own-train-lyrics"
|
||||
title := "The Silver Seas"
|
||||
artist := "Catch Yer Own Train"
|
||||
|
||||
r, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
l := logrus.New()
|
||||
m := New(l)
|
||||
|
||||
m.ServeHTTP(rr, r)
|
||||
|
||||
defer rr.Result().Body.Close()
|
||||
|
||||
doc, err := goquery.NewDocumentFromReader(rr.Result().Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
docTitle := doc.Find("#metadata-info > h2").Text()
|
||||
docArtist := doc.Find("#metadata-info > h1").Text()
|
||||
|
||||
if docTitle != title {
|
||||
t.Fatalf("expected %q, got %q\n", title, docTitle)
|
||||
}
|
||||
|
||||
if docArtist != artist {
|
||||
t.Fatalf("expected %q, got %q\n", artist, docArtist)
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@ -23,7 +24,7 @@ func isValidExt(ext string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func ImageProxy(l *logrus.Logger) http.HandlerFunc {
|
||||
func imageProxy(l *logrus.Logger) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
v := mux.Vars(r)
|
||||
f := v["filename"]
|
||||
@ -52,7 +53,7 @@ func ImageProxy(l *logrus.Logger) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-type", fmt.Sprintf("image/%s", ext))
|
||||
w.Header().Add("Content-type", mime.TypeByExtension("."+ext))
|
||||
w.Header().Add("Cache-Control", "max-age=1296000")
|
||||
if _, err = io.Copy(w, res.Body); err != nil {
|
||||
l.Errorln("unable to write image", err)
|
||||
|
38
handlers/proxy_test.go
Normal file
38
handlers/proxy_test.go
Normal file
@ -0,0 +1,38 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func TestImageProxy(t *testing.T) {
|
||||
imgURL := "/images/3852401ae6c6d6a51aafe814d67199f0.1000x1000x1.jpg"
|
||||
|
||||
r, err := http.NewRequest(http.MethodGet, imgURL, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
l := logrus.New()
|
||||
m := New(l)
|
||||
|
||||
m.ServeHTTP(rr, r)
|
||||
|
||||
cc := rr.Result().Header.Get("cache-control")
|
||||
maxAge := "max-age=1296000"
|
||||
ct := rr.Result().Header.Get("content-type")
|
||||
mimeType := mime.TypeByExtension(".jpg")
|
||||
|
||||
if cc != maxAge {
|
||||
t.Fatalf("expected %q, got %q\n", maxAge, cc)
|
||||
}
|
||||
|
||||
if ct != mimeType {
|
||||
t.Fatalf("expected %q, got %q\n", mimeType, ct)
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Search(l *logrus.Logger) http.HandlerFunc {
|
||||
func search(l *logrus.Logger) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query().Get("q")
|
||||
url := fmt.Sprintf(`https://genius.com/api/search/multi?q=%s`, url.QueryEscape(query))
|
||||
|
38
handlers/search_test.go
Normal file
38
handlers/search_test.go
Normal file
@ -0,0 +1,38 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func TestSearch(t *testing.T) {
|
||||
url := "/search?q=it+aint+hard+to+tell"
|
||||
artist := "Nas"
|
||||
|
||||
r, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
l := logrus.New()
|
||||
m := New(l)
|
||||
|
||||
m.ServeHTTP(rr, r)
|
||||
|
||||
defer rr.Result().Body.Close()
|
||||
|
||||
doc, err := goquery.NewDocumentFromReader(rr.Result().Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
docArtist := doc.Find("#search-item > div > span").First().Text()
|
||||
if docArtist != artist {
|
||||
t.Fatalf("expected %q, got %q\n", artist, docArtist)
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ import (
|
||||
fhttp "github.com/Danny-Dasilva/fhttp"
|
||||
)
|
||||
|
||||
func MustHeaders(next http.Handler) http.Handler {
|
||||
func mustHeaders(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
csp := "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; object-src 'none'"
|
||||
w.Header().Add("content-security-policy", csp)
|
||||
|
32
handlers/utils_test.go
Normal file
32
handlers/utils_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package handlers
|
||||
|
||||
/*
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSendRequest(t *testing.T) {
|
||||
res, err := sendRequest("https://tls.peet.ws/api/clean")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
type fingerprint struct {
|
||||
JA3 string `json:"ja3"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(res.Body)
|
||||
var fg fingerprint
|
||||
|
||||
if err := decoder.Decode(&fg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if fg.JA3 != JA3 {
|
||||
t.Fatalf("expected %q, got %q\n", JA3, fg.JA3)
|
||||
}
|
||||
}
|
||||
*/
|
25
main.go
25
main.go
@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -9,35 +8,15 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/rramiachraf/dumb/handlers"
|
||||
"github.com/rramiachraf/dumb/views"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var logger = logrus.New()
|
||||
|
||||
func main() {
|
||||
r := mux.NewRouter()
|
||||
|
||||
r.Use(handlers.MustHeaders)
|
||||
|
||||
r.Handle("/", templ.Handler(views.HomePage()))
|
||||
r.HandleFunc("/{id}-lyrics", handlers.Lyrics(logger)).Methods("GET")
|
||||
r.HandleFunc("/albums/{artist}/{albumName}", handlers.Album(logger)).Methods("GET")
|
||||
r.HandleFunc("/images/{filename}.{ext}", handlers.ImageProxy(logger)).Methods("GET")
|
||||
r.HandleFunc("/search", handlers.Search(logger)).Methods("GET")
|
||||
r.HandleFunc("/{id}/{artist-song}/{verse}/annotations", handlers.Annotations(logger)).Methods("GET")
|
||||
r.HandleFunc("/instances.json", handlers.Instances(logger)).Methods("GET")
|
||||
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
||||
r.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
views.ErrorPage(404, "page not found").Render(context.Background(), w)
|
||||
})
|
||||
var logger = logrus.New()
|
||||
|
||||
server := &http.Server{
|
||||
Handler: r,
|
||||
Handler: handlers.New(logger),
|
||||
WriteTimeout: 25 * time.Second,
|
||||
ReadTimeout: 25 * time.Second,
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user