require "rails_helper" RSpec.describe Api::SessionsController, type: :controller do let!(:user) { User.create!(nickname: "AdmiralNelson", password: "securepassword") } let(:token) { JwtService.encode(user_id: user.id) } describe "POST #create" do context "with valid credentials" do it "returns 200 OK with user info and JWT token" do post :create, params: { nickname: "AdmiralNelson", password: "securepassword" } expect(response).to have_http_status(:ok) json = JSON.parse(response.body) expect(json["token"]).to be_present expect(json["user"]).to include( "id" => user.id, "nickname" => "AdmiralNelson", "avatar_url" => nil ) decoded = JwtService.decode(json["token"]) expect(decoded[:user_id]).to eq(user.id) end end context "with invalid credentials" do it "returns 401 when password does not match" do post :create, params: { nickname: "AdmiralNelson", password: "wrongpassword" } expect(response).to have_http_status(:unauthorized) json = JSON.parse(response.body) expect(json["error"]).to eq("Invalid nickname or password") expect(json["token"]).to be_nil end it "returns 401 when user does not exist" do post :create, params: { nickname: "GhostCaptain", password: "securepassword" } expect(response).to have_http_status(:unauthorized) json = JSON.parse(response.body) expect(json["error"]).to eq("Invalid nickname or password") end it "returns 401 when nickname is omitted" do post :create, params: { password: "securepassword" } expect(response).to have_http_status(:unauthorized) json = JSON.parse(response.body) expect(json["error"]).to eq("Invalid nickname or password") end it "returns 401 when password is omitted" do post :create, params: { nickname: "AdmiralNelson" } expect(response).to have_http_status(:unauthorized) json = JSON.parse(response.body) expect(json["error"]).to eq("Invalid nickname or password") end it "returns 401 when credentials are blank" do post :create, params: { nickname: "", password: "" } expect(response).to have_http_status(:unauthorized) json = JSON.parse(response.body) expect(json["error"]).to eq("Invalid nickname or password") end end end describe "DELETE #destroy" do context "when authenticated" do it "returns 200 with success message" do request.headers["Authorization"] = "Bearer #{token}" delete :destroy expect(response).to have_http_status(:ok) json = JSON.parse(response.body) expect(json["message"]).to eq("Signed out successfully") end end context "when unauthenticated" do it "returns 401 unauthorized" do delete :destroy expect(response).to have_http_status(:unauthorized) json = JSON.parse(response.body) expect(json["error"]).to eq("Unauthorized") end end end describe "GET #me" do context "when authenticated with Authorization header" do it "returns 200 with user data" do request.headers["Authorization"] = "Bearer #{token}" get :me expect(response).to have_http_status(:ok) json = JSON.parse(response.body) expect(json["user"]).to include( "id" => user.id, "nickname" => "AdmiralNelson", "avatar_url" => nil ) end it "includes avatar_url when user has an attached avatar" do user.avatar.attach( io: StringIO.new("\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15c4"), filename: "avatar.png", content_type: "image/png" ) request.headers["Authorization"] = "Bearer #{token}" get :me expect(response).to have_http_status(:ok) json = JSON.parse(response.body) expect(json["user"]["avatar_url"]).to be_present end end context "when authenticated with token in query params" do it "returns 200 with user data" do get :me, params: { token: token } expect(response).to have_http_status(:ok) json = JSON.parse(response.body) expect(json["user"]["id"]).to eq(user.id) end end context "when unauthenticated" do it "returns 401 unauthorized" do get :me expect(response).to have_http_status(:unauthorized) json = JSON.parse(response.body) expect(json["error"]).to eq("Unauthorized") end end context "when token is expired" do it "returns 401 unauthorized" do expired_token = JwtService.encode(user_id: user.id, exp: 1.hour.ago.to_i) request.headers["Authorization"] = "Bearer #{expired_token}" get :me expect(response).to have_http_status(:unauthorized) end end end end