132 lines
5.1 KiB
Go
132 lines
5.1 KiB
Go
package app
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/DATA-DOG/go-sqlmock"
|
|
)
|
|
|
|
func TestTokenRoutes(t *testing.T) {
|
|
s, mock, cleanup := newMockServer(t)
|
|
defer cleanup()
|
|
user := User{ID: 1, Email: "alice@example.com", Username: "alice"}
|
|
token := "tok"
|
|
now := time.Now().UTC()
|
|
|
|
expectBearerUser(mock, token, user)
|
|
mock.ExpectQuery("SELECT id, created_at, expires_at, revoked_at").WithArgs(user.ID).WillReturnRows(sqlmock.NewRows([]string{"id", "created_at", "expires_at", "revoked_at", "active"}).AddRow(int64(9), now, now.Add(time.Hour), sql.NullTime{}, true))
|
|
rr := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodGet, "/api/tokens", nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
s.handleTokenRoutes(rr, req, "")
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("list status=%d body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
var infos []TokenInfo
|
|
if err := json.Unmarshal(rr.Body.Bytes(), &infos); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(infos) != 1 || infos[0].ID != 9 || !infos[0].Active {
|
|
t.Fatalf("unexpected token infos: %#v", infos)
|
|
}
|
|
|
|
expectBearerUser(mock, token, user)
|
|
mock.ExpectExec("UPDATE auth_tokens SET revoked_at").WithArgs(int64(9), user.ID).WillReturnResult(sqlmock.NewResult(0, 1))
|
|
rr = httptest.NewRecorder()
|
|
req = httptest.NewRequest(http.MethodDelete, "/api/tokens/9", nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
s.handleTokenRoutes(rr, req, "/9")
|
|
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "revoked") {
|
|
t.Fatalf("revoke status=%d body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
expectBearerUser(mock, token, user)
|
|
mock.ExpectExec("DELETE FROM auth_tokens WHERE user_id").WithArgs(user.ID).WillReturnResult(sqlmock.NewResult(0, 2))
|
|
rr = httptest.NewRecorder()
|
|
req = httptest.NewRequest(http.MethodPost, "/api/tokens/prune", nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
s.handleTokenRoutes(rr, req, "/prune")
|
|
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "pruned") {
|
|
t.Fatalf("prune status=%d body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
expectBearerUser(mock, token, user)
|
|
rr = httptest.NewRecorder()
|
|
req = httptest.NewRequest(http.MethodDelete, "/api/tokens/nope", nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
s.handleTokenRoutes(rr, req, "/nope")
|
|
if rr.Code != http.StatusBadRequest {
|
|
t.Fatalf("invalid id status=%d body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestAdminRoutes(t *testing.T) {
|
|
s, mock, cleanup := newMockServer(t)
|
|
defer cleanup()
|
|
admin := User{ID: 1, Email: "admin@example.com", Username: "admin", IsAdmin: true}
|
|
plain := User{ID: 2, Email: "bob@example.com", Username: "bob"}
|
|
token := "tok"
|
|
|
|
expectBearerUser(mock, token, plain)
|
|
rr := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodGet, "/api/admin/users", nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
s.handleAdminRoutes(rr, req, "/users")
|
|
if rr.Code != http.StatusForbidden {
|
|
t.Fatalf("non-admin status=%d body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
expectBearerUser(mock, token, admin)
|
|
mock.ExpectQuery("SELECT id, email, username, is_admin FROM users").WillReturnRows(sqlmock.NewRows([]string{"id", "email", "username", "is_admin"}).AddRow(int64(1), "admin@example.com", "admin", true).AddRow(int64(2), "bob@example.com", "bob", false))
|
|
rr = httptest.NewRecorder()
|
|
req = httptest.NewRequest(http.MethodGet, "/api/admin/users", nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
s.handleAdminRoutes(rr, req, "/users")
|
|
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "bob@example.com") {
|
|
t.Fatalf("users status=%d body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
expectBearerUser(mock, token, admin)
|
|
rr = httptest.NewRecorder()
|
|
req = httptest.NewRequest(http.MethodDelete, "/api/admin/users/admin", nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
s.handleAdminRoutes(rr, req, "/users/admin")
|
|
if rr.Code != http.StatusBadRequest {
|
|
t.Fatalf("delete self status=%d body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
expectBearerUser(mock, token, admin)
|
|
mock.ExpectExec("DELETE FROM users").WithArgs("bob").WillReturnResult(sqlmock.NewResult(0, 1))
|
|
rr = httptest.NewRecorder()
|
|
req = httptest.NewRequest(http.MethodDelete, "/api/admin/users/bob", nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
s.handleAdminRoutes(rr, req, "/users/bob")
|
|
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "deleted") {
|
|
t.Fatalf("delete status=%d body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
expectBearerUser(mock, token, admin)
|
|
mock.ExpectExec("DELETE FROM auth_tokens WHERE expires_at").WillReturnResult(sqlmock.NewResult(0, 1))
|
|
rr = httptest.NewRecorder()
|
|
req = httptest.NewRequest(http.MethodPost, "/api/admin/tokens/prune", nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
s.handleAdminRoutes(rr, req, "/tokens/prune")
|
|
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "pruned") {
|
|
t.Fatalf("admin prune status=%d body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|