# frozen_string_literal: true

describe "GitHub Oauth2" do
  let(:access_token) { "github_access_token_448" }
  let(:client_id) { "abcdef11223344" }
  let(:client_secret) { "adddcccdddd99922" }
  let(:temp_code) { "github_temp_code_544254" }

  fab!(:user1) { Fabricate(:user) }
  fab!(:user2) { Fabricate(:user) }

  def setup_github_emails_stub(emails)
    stub_request(:get, "https://api.github.com/user/emails").with(
      headers: {
        "Authorization" => "Bearer #{access_token}",
      },
    ).to_return(
      status: 200,
      body: JSON.dump(emails),
      headers: {
        "Content-Type" => "application/json",
      },
    )
  end

  before do
    SiteSetting.enable_github_logins = true
    SiteSetting.github_client_id = client_id
    SiteSetting.github_client_secret = client_secret

    stub_request(:post, "https://github.com/login/oauth/access_token").with(
      body:
        hash_including(
          "client_id" => client_id,
          "client_secret" => client_secret,
          "code" => temp_code,
        ),
    ).to_return(
      status: 200,
      body:
        Rack::Utils.build_query(
          access_token: access_token,
          scope: "user:email",
          token_type: "bearer",
        ),
      headers: {
        "Content-Type" => "application/x-www-form-urlencoded",
      },
    )

    stub_request(:get, "https://api.github.com/user").with(
      headers: {
        "Authorization" => "Bearer #{access_token}",
      },
    ).to_return(
      status: 200,
      body:
        JSON.dump(
          login: "octocat",
          id: 1,
          node_id: "MDQ6VXNlcjE=",
          avatar_url: "https://github.com/images/error/octocat_happy.gif",
          gravatar_id: "",
          url: "https://api.github.com/users/octocat",
          html_url: "https://github.com/octocat",
          followers_url: "https://api.github.com/users/octocat/followers",
          following_url: "https://api.github.com/users/octocat/following{/other_user}",
          gists_url: "https://api.github.com/users/octocat/gists{/gist_id}",
          starred_url: "https://api.github.com/users/octocat/starred{/owner}{/repo}",
          subscriptions_url: "https://api.github.com/users/octocat/subscriptions",
          organizations_url: "https://api.github.com/users/octocat/orgs",
          repos_url: "https://api.github.com/users/octocat/repos",
          events_url: "https://api.github.com/users/octocat/events{/privacy}",
          received_events_url: "https://api.github.com/users/octocat/received_events",
          type: "User",
          site_admin: false,
          name: "monalisa octocat",
          company: "GitHub",
          blog: "https://github.com/blog",
          location: "San Francisco",
          email: "octocat@github.com",
          hireable: false,
          bio: "There once was...",
          twitter_username: "monatheoctocat",
          public_repos: 2,
          public_gists: 1,
          followers: 20,
          following: 0,
          created_at: "2008-01-14T04:33:35Z",
          updated_at: "2008-01-14T04:33:35Z",
          private_gists: 81,
          total_private_repos: 100,
          owned_private_repos: 100,
          disk_usage: 10_000,
          collaborators: 8,
          two_factor_authentication: true,
          plan: {
            name: "Medium",
            space: 400,
            private_repos: 20,
            collaborators: 0,
          },
        ),
      headers: {
        "Content-Type" => "application/json",
      },
    )
  end

  it "doesn't sign in anyone if none of the emails from github are verified" do
    post "/auth/github"
    expect(response.status).to eq(302)
    expect(response.location).to start_with("https://github.com/login/oauth/authorize?")

    setup_github_emails_stub(
      [
        { email: user1.email, primary: true, verified: false, visibility: "private" },
        { email: user2.email, primary: false, verified: false, visibility: "private" },
      ],
    )

    post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code }
    expect(response.status).to eq(302)
    expect(response.location).to eq("http://test.localhost/")
    expect(session[:current_user_id]).to be_blank
  end

  it "matches a non-primary email if it's verified and the primary email isn't" do
    post "/auth/github"
    expect(response.status).to eq(302)
    expect(response.location).to start_with("https://github.com/login/oauth/authorize?")

    setup_github_emails_stub(
      [
        { email: user1.email, primary: true, verified: false, visibility: "private" },
        { email: user2.email, primary: false, verified: true, visibility: "private" },
      ],
    )

    post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code }
    expect(response.status).to eq(302)
    expect(response.location).to eq("http://test.localhost/")
    expect(session[:current_user_id]).to eq(user2.id)
  end

  it "doesn't match a non-primary email if it's not verified" do
    post "/auth/github"
    expect(response.status).to eq(302)
    expect(response.location).to start_with("https://github.com/login/oauth/authorize?")

    setup_github_emails_stub(
      [
        {
          email: "somerandomemail@discourse.org",
          primary: true,
          verified: true,
          visibility: "private",
        },
        { email: user2.email, primary: false, verified: false, visibility: "private" },
      ],
    )

    post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code }
    expect(response.status).to eq(302)
    expect(response.location).to eq("http://test.localhost/")
    expect(session[:current_user_id]).to be_blank
  end

  it "favors the primary email over secondary emails when they're all verified" do
    post "/auth/github"
    expect(response.status).to eq(302)
    expect(response.location).to start_with("https://github.com/login/oauth/authorize?")

    setup_github_emails_stub(
      [
        { email: user1.email, primary: true, verified: true, visibility: "private" },
        { email: user2.email, primary: false, verified: true, visibility: "private" },
      ],
    )

    post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code }
    expect(response.status).to eq(302)
    expect(response.location).to eq("http://test.localhost/")
    expect(session[:current_user_id]).to eq(user1.id)
  end
end