Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
2 changes: 1 addition & 1 deletion .bundle/config
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
BUNDLE_BIN: "bin"
BUNDLE_PATH: "vendor/gems"
BUNDLE_PATH: "/home/runner/work/hooks/hooks/vendor/bundle"
BUNDLE_CACHE_PATH: "vendor/cache"
BUNDLE_CACHE_ALL: "true"
BUNDLE_SPECIFIC_PLATFORM: "true"
Expand Down
201 changes: 201 additions & 0 deletions spec/unit/lib/hooks/app/auth/auth_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# frozen_string_literal: true

describe Hooks::App::Auth do
# Create a test class that includes the auth module
let(:test_class) do
Class.new do
include Hooks::App::Auth

def error!(message, code)
raise StandardError, "#{message} (#{code})"
end
end
end

let(:auth_instance) { test_class.new }
let(:payload) { "test payload" }
let(:headers) { { "Content-Type" => "application/json" } }

describe "#validate_auth!" do
context "when auth config has secret_env_key" do
let(:endpoint_config) do
{
auth: {
type: "hmac",
secret_env_key: "TEST_SECRET"
}
}
end

context "when secret exists in environment" do
before do
ENV["TEST_SECRET"] = "test-secret-value"
end

after do
ENV.delete("TEST_SECRET")
end

context "with HMAC auth type" do
it "validates with HMAC plugin when authentication succeeds" do
allow(Hooks::Plugins::Auth::HMAC).to receive(:valid?).and_return(true)

expect { auth_instance.validate_auth!(payload, headers, endpoint_config) }
.not_to raise_error

expect(Hooks::Plugins::Auth::HMAC).to have_received(:valid?).with(
payload: payload,
headers: headers,
secret: "test-secret-value",
config: endpoint_config
)
end

it "raises authentication failed error when HMAC validation fails" do
allow(Hooks::Plugins::Auth::HMAC).to receive(:valid?).and_return(false)

expect { auth_instance.validate_auth!(payload, headers, endpoint_config) }
.to raise_error(StandardError, "authentication failed (401)")
end
end

context "with shared_secret auth type" do
let(:endpoint_config) do
{
auth: {
type: "shared_secret",
secret_env_key: "TEST_SECRET"
}
}
end

it "validates with SharedSecret plugin when authentication succeeds" do
allow(Hooks::Plugins::Auth::SharedSecret).to receive(:valid?).and_return(true)

expect { auth_instance.validate_auth!(payload, headers, endpoint_config) }
.not_to raise_error

expect(Hooks::Plugins::Auth::SharedSecret).to have_received(:valid?).with(
payload: payload,
headers: headers,
secret: "test-secret-value",
config: endpoint_config
)
end

it "raises authentication failed error when SharedSecret validation fails" do
allow(Hooks::Plugins::Auth::SharedSecret).to receive(:valid?).and_return(false)

expect { auth_instance.validate_auth!(payload, headers, endpoint_config) }
.to raise_error(StandardError, "authentication failed (401)")
end
end

context "with unsupported auth type" do
let(:endpoint_config) do
{
auth: {
type: "custom",
secret_env_key: "TEST_SECRET"
}
}
end

it "raises custom validators not implemented error" do
expect { auth_instance.validate_auth!(payload, headers, endpoint_config) }
.to raise_error(StandardError, "Custom validators not implemented in POC (500)")
end
end

context "with case variations in auth type" do
let(:endpoint_config) do
{
auth: {
type: "HMAC",
secret_env_key: "TEST_SECRET"
}
}
end

it "handles uppercase auth type" do
allow(Hooks::Plugins::Auth::HMAC).to receive(:valid?).and_return(true)

expect { auth_instance.validate_auth!(payload, headers, endpoint_config) }
.not_to raise_error
end
end
end

context "when secret does not exist in environment" do
let(:endpoint_config) do
{
auth: {
type: "hmac",
secret_env_key: "NONEXISTENT_SECRET"
}
}
end

it "raises secret not found error" do
ENV.delete("NONEXISTENT_SECRET") # Ensure it's not set

expect { auth_instance.validate_auth!(payload, headers, endpoint_config) }
.to raise_error(StandardError, "secret 'NONEXISTENT_SECRET' not found in environment (500)")
end
end
end

context "when auth config has no secret_env_key" do
let(:endpoint_config) do
{
auth: {
type: "hmac"
}
}
end

it "returns without validation" do
expect { auth_instance.validate_auth!(payload, headers, endpoint_config) }
.not_to raise_error

# No auth plugins should be called
expect(Hooks::Plugins::Auth::HMAC).not_to receive(:valid?)
expect(Hooks::Plugins::Auth::SharedSecret).not_to receive(:valid?)
end
end

context "when auth config has nil secret_env_key" do
let(:endpoint_config) do
{
auth: {
type: "hmac",
secret_env_key: nil
}
}
end

it "returns without validation" do
expect { auth_instance.validate_auth!(payload, headers, endpoint_config) }
.not_to raise_error
end
end

context "when auth config has empty secret_env_key" do
let(:endpoint_config) do
{
auth: {
type: "hmac",
secret_env_key: ""
}
}
end

it "raises secret not found error for empty string" do
ENV.delete("") # Ensure empty string key is not set

expect { auth_instance.validate_auth!(payload, headers, endpoint_config) }
.to raise_error(StandardError, "secret '' not found in environment (500)")
end
end
end
end
66 changes: 66 additions & 0 deletions spec/unit/lib/hooks/app/endpoints/health_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# frozen_string_literal: true

describe Hooks::App::HealthEndpoint do
# Test the endpoint behavior using a mock API instance
let(:api_instance) do
Class.new(Grape::API) do
mount Hooks::App::HealthEndpoint
end
end

before do
# Mock the API start time
allow(Hooks::App::API).to receive(:start_time).and_return(Time.parse("2025-01-01T00:00:00Z"))
end

describe "GET /" do
let(:response) { api_instance.new.call(Rack::MockRequest.env_for("/")) }

it "returns 200 status" do
expect(response[0]).to eq(200)
end

it "returns JSON content type" do
headers = response[1]
expect(headers["Content-Type"]).to include("application/json")
end

it "returns health status information" do
body = JSON.parse(response[2].first)

expect(body["status"]).to eq("healthy")
expect(body["timestamp"]).to eq(TIME_MOCK)
expect(body["version"]).to eq(Hooks::VERSION)
expect(body["uptime_seconds"]).to be_a(Integer)
expect(body["uptime_seconds"]).to eq(0) # Since mocked time is the same as start time
end

it "calculates uptime correctly" do
# Set different start time to test uptime calculation
start_time = Time.parse("2024-12-31T23:59:30Z")
allow(Hooks::App::API).to receive(:start_time).and_return(start_time)

body = JSON.parse(response[2].first)
expect(body["uptime_seconds"]).to eq(30) # 30 seconds difference
end

it "includes all required fields" do
body = JSON.parse(response[2].first)

expect(body).to have_key("status")
expect(body).to have_key("timestamp")
expect(body).to have_key("version")
expect(body).to have_key("uptime_seconds")
end

it "returns valid JSON" do
expect { JSON.parse(response[2].first) }.not_to raise_error
end
end

describe "inheritance" do
it "inherits from Grape::API" do
expect(described_class.superclass).to eq(Grape::API)
end
end
end
62 changes: 62 additions & 0 deletions spec/unit/lib/hooks/app/endpoints/version_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# frozen_string_literal: true

describe Hooks::App::VersionEndpoint do
# Test the endpoint behavior using a mock API instance
let(:api_instance) do
Class.new(Grape::API) do
mount Hooks::App::VersionEndpoint
end
end

describe "GET /" do
let(:response) { api_instance.new.call(Rack::MockRequest.env_for("/")) }

it "returns 200 status" do
expect(response[0]).to eq(200)
end

it "returns JSON content type" do
headers = response[1]
expect(headers["Content-Type"]).to include("application/json")
end

it "returns version information" do
body = JSON.parse(response[2].first)

expect(body["version"]).to eq(Hooks::VERSION)
expect(body["timestamp"]).to eq(TIME_MOCK)
end

it "includes all required fields" do
body = JSON.parse(response[2].first)

expect(body).to have_key("version")
expect(body).to have_key("timestamp")
end

it "has exactly two fields" do
body = JSON.parse(response[2].first)
expect(body.keys.length).to eq(2)
end

it "returns valid JSON" do
expect { JSON.parse(response[2].first) }.not_to raise_error
end

it "returns current version from Hooks::VERSION" do
body = JSON.parse(response[2].first)
expect(body["version"]).to match(/^\d+\.\d+\.\d+$/)
end

it "returns timestamp in ISO 8601 format" do
body = JSON.parse(response[2].first)
expect(body["timestamp"]).to match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/)
end
end

describe "inheritance" do
it "inherits from Grape::API" do
expect(described_class.superclass).to eq(Grape::API)
end
end
end
Loading
Loading