package server_test import ( "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "level3.git.onstackit.cloud/LEVEL3/gabriel/src/api" "level3.git.onstackit.cloud/LEVEL3/gabriel/src/auth" "level3.git.onstackit.cloud/LEVEL3/gabriel/src/server/mock" ) // The auth package keeps users in a process-global in-memory store. To keep // tests independent without refactoring that package, each test registers a // uniquely-named user. This mirrors the approach taken in // src/auth/auth_test.go. func TestLoginUser_badBody(t *testing.T) { pdb, srch := &mock.FakePagedbService{}, &mock.FakeSearchService{} srv, _ := newTestServer(t, pdb, srch, "") rec := httptest.NewRecorder() r := httptest.NewRequest(http.MethodPost, "/v1/auth/login", strings.NewReader("{not json")) srv.LoginUser(rec, r) assertStatus(t, rec, http.StatusBadRequest) } func TestLoginUser_unknownUser(t *testing.T) { pdb, srch := &mock.FakePagedbService{}, &mock.FakeSearchService{} srv, _ := newTestServer(t, pdb, srch, "") rec := httptest.NewRecorder() r := newJSONRequest(t, http.MethodPost, "/v1/auth/login", api.AuthRequest{Username: "no-such-user-xyz", Password: "x"}) srv.LoginUser(rec, r) assertStatus(t, rec, http.StatusUnauthorized) assertJSONBody(t, rec, map[string]string{"error": "invalid username or password"}) } func TestLoginUser_wrongPassword(t *testing.T) { username := "login-wrongpw-" + t.Name() if err := auth.Register(auth.UserData{Username: username, Password: "correct"}); err != nil { t.Fatal(err) } pdb, srch := &mock.FakePagedbService{}, &mock.FakeSearchService{} srv, _ := newTestServer(t, pdb, srch, "") rec := httptest.NewRecorder() r := newJSONRequest(t, http.MethodPost, "/v1/auth/login", api.AuthRequest{Username: username, Password: "wrong"}) srv.LoginUser(rec, r) assertStatus(t, rec, http.StatusUnauthorized) assertJSONBody(t, rec, map[string]string{"error": "invalid username or password"}) } func TestLoginUser_success(t *testing.T) { username := "login-ok-" + t.Name() if err := auth.Register(auth.UserData{Username: username, Password: "secret"}); err != nil { t.Fatal(err) } pdb, srch := &mock.FakePagedbService{}, &mock.FakeSearchService{} srv, _ := newTestServer(t, pdb, srch, "") rec := httptest.NewRecorder() r := newJSONRequest(t, http.MethodPost, "/v1/auth/login", api.AuthRequest{Username: username, Password: "secret"}) srv.LoginUser(rec, r) assertStatus(t, rec, http.StatusOK) var got api.AuthResponse decodeBody(t, rec, &got) if got.Role != api.Default { t.Fatalf("role = %s, want %s", got.Role, api.Default) } if got.Token == "" { t.Fatal("token empty") } } func TestRegisterUser_badBody(t *testing.T) { pdb, srch := &mock.FakePagedbService{}, &mock.FakeSearchService{} srv, _ := newTestServer(t, pdb, srch, "") rec := httptest.NewRecorder() r := httptest.NewRequest(http.MethodPost, "/v1/auth/register", strings.NewReader("garbage")) srv.RegisterUser(rec, r) assertStatus(t, rec, http.StatusBadRequest) } func TestRegisterUser_inviteRequired_missing(t *testing.T) { pdb, srch := &mock.FakePagedbService{}, &mock.FakeSearchService{} srv, _ := newTestServer(t, pdb, srch, "sunset-rocket-42") rec := httptest.NewRecorder() r := newJSONRequest(t, http.MethodPost, "/v1/auth/register", api.AuthRequest{Username: "invite-missing-" + t.Name(), Password: "p"}) srv.RegisterUser(rec, r) assertStatus(t, rec, http.StatusForbidden) assertJSONBody(t, rec, map[string]string{"error": "invalid invite code"}) } func TestRegisterUser_inviteRequired_wrong(t *testing.T) { pdb, srch := &mock.FakePagedbService{}, &mock.FakeSearchService{} srv, _ := newTestServer(t, pdb, srch, "sunset-rocket-42") rec := httptest.NewRecorder() r := newJSONRequest(t, http.MethodPost, "/v1/auth/register", api.AuthRequest{Username: "invite-wrong-" + t.Name(), Password: "p", InviteCode: new("nope")}) srv.RegisterUser(rec, r) assertStatus(t, rec, http.StatusForbidden) assertJSONBody(t, rec, map[string]string{"error": "invalid invite code"}) } func TestRegisterUser_success(t *testing.T) { username := "register-ok-" + t.Name() pdb, srch := &mock.FakePagedbService{}, &mock.FakeSearchService{} srv, _ := newTestServer(t, pdb, srch, "") rec := httptest.NewRecorder() r := newJSONRequest(t, http.MethodPost, "/v1/auth/register", api.AuthRequest{Username: username, Password: "secret"}) srv.RegisterUser(rec, r) assertStatus(t, rec, http.StatusCreated) var got api.AuthResponse decodeBody(t, rec, &got) if got.Role != api.Default { t.Fatalf("role = %s, want %s", got.Role, api.Default) } if got.Token == "" { t.Fatal("token empty") } // The user should now be able to log in via the login handler too. rec2 := httptest.NewRecorder() r2 := newJSONRequest(t, http.MethodPost, "/v1/auth/login", api.AuthRequest{Username: username, Password: "secret"}) srv.LoginUser(rec2, r2) assertStatus(t, rec2, http.StatusOK) } func TestRegisterUser_success_withInvite(t *testing.T) { username := "register-invite-ok-" + t.Name() pdb, srch := &mock.FakePagedbService{}, &mock.FakeSearchService{} srv, _ := newTestServer(t, pdb, srch, "sunset-rocket-42") rec := httptest.NewRecorder() r := newJSONRequest(t, http.MethodPost, "/v1/auth/register", api.AuthRequest{Username: username, Password: "secret", InviteCode: new("sunset-rocket-42")}) srv.RegisterUser(rec, r) assertStatus(t, rec, http.StatusCreated) var got api.AuthResponse decodeBody(t, rec, &got) if got.Token == "" { t.Fatal("token empty") } } // decodeBody unmarshals rec.Body into v. func decodeBody(t *testing.T, rec *httptest.ResponseRecorder, v any) { t.Helper() if err := json.Unmarshal(rec.Body.Bytes(), v); err != nil { t.Fatalf("decode body %q: %v", rec.Body.String(), err) } }