summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--handlers.go51
-rw-r--r--handlers_test.go82
-rw-r--r--main.go5
-rw-r--r--password_test.go2
4 files changed, 119 insertions, 21 deletions
diff --git a/handlers.go b/handlers.go
index d28a491..e6d60fb 100644
--- a/handlers.go
+++ b/handlers.go
@@ -1,7 +1,9 @@
package main
import (
+ "database/sql"
"embed"
+ "encoding/json"
"fmt"
"io/fs"
"log"
@@ -13,8 +15,9 @@ import (
//go:embed build/frontend/public
var frontendEmbed embed.FS
-func registerHandlers(r *mux.Router) {
- r.HandleFunc("/api/version", version)
+func registerHandlers(r *mux.Router, db *sql.DB) {
+ r.Handle("/api/version", version(db))
+ r.Handle("/api/registration", register(db))
// frontend must come last to make sure /api takes precedence
frontend, err := fs.Sub(frontendEmbed, "build/frontend/public")
@@ -24,6 +27,46 @@ func registerHandlers(r *mux.Router) {
r.PathPrefix("/").Handler(http.FileServerFS(frontend))
}
-func version(w http.ResponseWriter, r *http.Request) {
- fmt.Fprint(w, versionTxt)
+func version(db *sql.DB) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprint(w, versionTxt)
+ })
+}
+
+func register(db *sql.DB) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ var data struct {
+ Username string
+ Password string
+ }
+
+ dec := json.NewDecoder(r.Body)
+ dec.DisallowUnknownFields()
+
+ err := dec.Decode(&data)
+ if err != nil {
+ log.Printf("Failed to decode: %v", err)
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ if data.Username == "" {
+ log.Print("Got registration request with empty username")
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ if data.Password == "" {
+ log.Print("Got registration request with empty password")
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ err = Register(db, data.Username, data.Password)
+ if err != nil {
+ log.Printf("Failed to register new user: %v", err)
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ })
}
diff --git a/handlers_test.go b/handlers_test.go
index 4e28de7..3e226d0 100644
--- a/handlers_test.go
+++ b/handlers_test.go
@@ -1,30 +1,86 @@
package main
import (
+ "bytes"
+ "database/sql"
"net/http"
"net/http/httptest"
"testing"
)
+type Handler func(db *sql.DB) http.Handler
+
+type httpCase struct {
+ name string
+ method string
+ input []byte
+ output []byte
+ status int
+}
+
+func testHandler(
+ t *testing.T,
+ handler Handler,
+ cases []httpCase,
+ setup func(t *testing.T) *sql.DB,
+) {
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ db := setup(t)
+
+ in := bytes.NewReader(c.input)
+
+ rr := httptest.NewRecorder()
+
+ req, err := http.NewRequest("POST", "/", in)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ handler(db).ServeHTTP(rr, req)
+
+ if rr.Code != c.status {
+ t.Errorf("handler returned wrong status code: got %v want %v",
+ rr.Code, c.status)
+ }
+
+ if rr.Body.String() != string(c.output) {
+ t.Errorf("handler returned unexpected body: got %v want %v",
+ rr.Body.String(), string(c.output))
+ }
+ })
+ }
+}
+
func TestHTTPVersion(t *testing.T) {
- req, err := http.NewRequest("GET", "/", nil)
- if err != nil {
- t.Fatal(err)
+ cases := []httpCase{
+ {"basic", "GET", []byte(``), []byte(versionTxt), http.StatusOK},
}
- rr := httptest.NewRecorder()
- handler := http.HandlerFunc(version)
+ setup := func(t *testing.T) *sql.DB {
+ return nil
+ }
- handler.ServeHTTP(rr, req)
+ testHandler(t, version, cases, setup)
+}
- if status := rr.Code; status != http.StatusOK {
- t.Errorf("handler returned wrong status code: got %v want %v",
- status, http.StatusOK)
+func TestHTTPRegister(t *testing.T) {
+ cases := []httpCase{
+ {"basic", "POST", []byte(`{"username":"testuser","password":"mypass"}`), []byte(""), http.StatusOK},
+ {"missing-password", "POST", []byte(`{"username":"testuser"}`), []byte(""), http.StatusBadRequest},
+ {"missing-username", "POST", []byte(`{"password":"mypass"}`), []byte(""), http.StatusBadRequest},
+ {"unknown-field", "POST", []byte(`{"username":"testuser","password":"mypass","foo":"bar"}`), []byte(""), http.StatusBadRequest},
+ {"invalid-json", "POST", []byte(`{username":"testuser","password":"mypass"}`), []byte(""), http.StatusBadRequest},
}
- expected := versionTxt
- if rr.Body.String() != expected {
- t.Errorf("handler returned unexpected body: got %v want %v",
- rr.Body.String(), expected)
+ setup := func(t *testing.T) *sql.DB {
+ db, _ := emptyDB(t)
+ err := MigrateToLatest(db)
+ if err != nil {
+ t.Fatalf("Could not migrate test database: %v", err)
+ }
+ return db
}
+
+ testHandler(t, register, cases, setup)
}
diff --git a/main.go b/main.go
index d0c4763..4cd1150 100644
--- a/main.go
+++ b/main.go
@@ -24,7 +24,6 @@ const (
//go:embed VERSION.txt
var versionTxt string
var router *mux.Router = mux.NewRouter()
-var db *sql.DB
func init() {
log.SetFlags(0)
@@ -53,7 +52,7 @@ func run(
defer log.Println("finserv finance server was stopped.")
var err error
- db, err = sql.Open("sqlite3", dbPath)
+ db, err := sql.Open("sqlite3", dbPath)
if err != nil {
log.Fatalf("Failed to open SQLite3 database '%s': %v", dbPath, err)
}
@@ -64,7 +63,7 @@ func run(
log.Fatalf("Failed to migrate SQLite3 database '%s': %v", dbPath, err)
}
- registerHandlers(router)
+ registerHandlers(router, db)
server := &http.Server{
Handler: handler,
diff --git a/password_test.go b/password_test.go
index 9d89b8f..a577194 100644
--- a/password_test.go
+++ b/password_test.go
@@ -3,8 +3,8 @@ package main
import (
"testing"
- "golang.org/x/crypto/bcrypt"
_ "github.com/mattn/go-sqlite3"
+ "golang.org/x/crypto/bcrypt"
)
var passwords = []struct {