Skip to content

Commit

Permalink
Refactor test suites
Browse files Browse the repository at this point in the history
  • Loading branch information
binos30 committed Jan 11, 2025
1 parent 2ea2d0a commit e77ef0a
Show file tree
Hide file tree
Showing 31 changed files with 473 additions and 214 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,5 @@ yarn-debug.log*
.ionide

# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode

coverage
1 change: 0 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
"github.vscode-github-actions",
"hossaini.bootstrap-intellisense",
"redhat.vscode-yaml",
"ruby-syntax-tree.vscode-syntax-tree",
"shopify.ruby-lsp",
"sianglim.slim"
]
Expand Down
14 changes: 12 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,15 @@ group :development, :test do
# Testing Framework
gem "rspec-rails"

# A fixtures replacement with a straightforward definition syntax, support for multiple build strategies [https://github.com/thoughtbot/factory_bot_rails]
gem "factory_bot_rails"

# Static analysis for security vulnerabilities [https://brakemanscanner.org/]
gem "brakeman", require: false

# Optimize queries [https://github.com/flyerhzm/bullet]
gem "bullet"

## Code Formatting & Linting
# Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/]
gem "rubocop-rails-omakase", require: false
Expand Down Expand Up @@ -93,7 +99,11 @@ group :development do
# A normaliser/beautifier for HTML that also understands embedded Ruby. Ideal for tidying up Rails templates
# [https://github.com/threedaymonk/htmlbeautifier]
gem "htmlbeautifier", require: false
end

# Optimize queries
gem "bullet"
group :test do
gem "shoulda-matchers", "~> 6.0"

# A code coverage analysis tool for Ruby [https://github.com/simplecov-ruby/simplecov]
gem "simplecov", require: false
end
17 changes: 17 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,19 @@ GEM
irb (~> 1.10)
reline (>= 0.3.8)
diff-lcs (1.5.1)
docile (1.4.1)
dotenv (3.1.4)
dotenv-rails (3.1.4)
dotenv (= 3.1.4)
railties (>= 6.1)
drb (2.2.1)
e2mmap (0.1.0)
erubi (1.13.0)
factory_bot (6.5.0)
activesupport (>= 5.0.0)
factory_bot_rails (6.4.4)
factory_bot (~> 6.5)
railties (>= 5.0.0)
globalid (1.2.1)
activesupport (>= 6.1)
htmlbeautifier (1.4.3)
Expand Down Expand Up @@ -269,6 +275,14 @@ GEM
rubocop-rspec (~> 3, >= 3.0.1)
ruby-progressbar (1.13.0)
securerandom (0.3.1)
shoulda-matchers (6.4.0)
activesupport (>= 5.2.0)
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.13.1)
simplecov_json_formatter (0.1.4)
slim (5.2.1)
temple (~> 0.10.0)
tilt (>= 2.1.0)
Expand Down Expand Up @@ -347,6 +361,7 @@ DEPENDENCIES
cssbundling-rails
debug
dotenv-rails
factory_bot_rails
htmlbeautifier
jbuilder
jsbundling-rails
Expand All @@ -357,6 +372,8 @@ DEPENDENCIES
rubocop-rails-omakase
rubocop-rspec
rubocop-rspec_rails
shoulda-matchers (~> 6.0)
simplecov
slim-rails
slim_lint
solargraph
Expand Down
70 changes: 66 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,32 @@ bin/dev

Go to [http://localhost:3000](http://localhost:3000)

## GitHub Actions, Linting and Security Auditing

GitHub actions are setup to lint and test the application on pushes to **main** and **feature** branches.

You can also run these actions locally before pushing to see if your run is likely to fail. See the following gems / commands for more info.

- [Brakeman](https://brakemanscanner.org/) - Security audit application code

```bash
bin/brakeman --no-pager
```

- [Brakeman: Ignoring False Positives](https://brakemanscanner.org/docs/ignoring_false_positives) - Creating and Managing an Ignore File

```bash
bin/brakeman -I --no-pager
```

- [Rubocop Rails Omakase](https://github.com/rails/rubocop-rails-omakase) - Ruby Linter

```bash
bin/rubocop
```

**Note:** Some linters like `ESLint`, `Prettier`, etc. will automatically run on `pre-commit` git hook.

## Testing

Setup test database
Expand All @@ -45,6 +71,12 @@ Default: Run all spec files (i.e., those matching spec/\*\*/\*\_spec.rb)
bin/rspec
```

or with `--fail-fast` option to stop running the test suite on the first failed test. You may add a parameter to tell RSpec to stop running the test suite after N failed tests, for example: `--fail-fast=3`

```bash
bin/rspec --fail-fast
```

Run all spec files in a single directory (recursively)

```bash
Expand All @@ -57,16 +89,20 @@ Run a single spec file
bin/rspec spec/models/entrance_spec.rb
```

Use the plain-English descriptions to generate a report of where the application conforms to (or fails to meet) the spec
Run a single example from a spec file (by line number)

```bash
bin/rspec --format documentation spec/models/entrance_spec.rb
bin/rspec spec/models/entrance_spec.rb:6
```

Run a single example from a spec file (by line number)
Use the plain-English descriptions to generate a report of where the application conforms to (or fails to meet) the spec

```bash
bin/rspec spec/models/entrance_spec.rb:6
bin/rspec --format documentation
```

```bash
bin/rspec --format documentation spec/models/entrance_spec.rb
```

See all options for running specs
Expand All @@ -75,6 +111,32 @@ See all options for running specs
bin/rspec --help
```

## Code Coverage

[Coverage]: https://docs.ruby-lang.org/en/3.3/Coverage.html "API doc for Ruby's Coverage library"
[SimpleCov]: https://github.com/simplecov-ruby/simplecov "A code coverage analysis tool for Ruby"

[SimpleCov][SimpleCov] is a code coverage analysis tool for Ruby. It uses [Ruby's built-in Coverage][Coverage] library to
gather code coverage data, but makes processing its results much easier by providing a clean API to filter, group, merge, format,
and display those results, giving you a complete code coverage suite that can be set up with just a couple lines of code.
SimpleCov/Coverage track covered ruby code, gathering coverage for common templating solutions like erb, slim and haml is not supported.

After running your tests, open `coverage/index.html` in the browser of your choice. For example, in a Mac Terminal,
run the following command from your application's root directory:

```bash
open coverage/index.html
```

in a debian/ubuntu Terminal,

```bash
xdg-open coverage/index.html
```

**Note:** [This guide](https://dwheeler.com/essays/open-files-urls.html) can help if you're unsure which command your particular
operating system requires.

## Modules

`Dashboard` - Monitors the occupancy of the parking spaces
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/api/v1/bookings_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class BookingsController < ApiController

# GET /api/v1/bookings or /api/v1/bookings.json
def index
@bookings = Booking.includes([:parking_slot]).order(:created_at)
@bookings = Booking.order(:created_at)
end

# GET /api/v1/bookings/1 or /api/v1/bookings/1.json
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/api/v1/parking_slots_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module V1
class ParkingSlotsController < ApiController
# GET /api/v1/parking_slots.json
def index
@parking_slots = ParkingSlot.includes([:parking_lot]).order(:id)
@parking_slots = ParkingSlot.order(:id)
end
end
end
Expand Down
17 changes: 13 additions & 4 deletions app/models/booking.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,26 @@
class Booking < ApplicationRecord
enum :vehicle_type, { small: 0, medium: 1, large: 2 }

belongs_to :parking_slot
belongs_to :parking_slot, inverse_of: :bookings

validates :vehicle_type, presence: true, inclusion: { in: vehicle_types.keys }
validates :plate_number, presence: true, length: { minimum: 2, maximum: 9 }
validates :vehicle_type, inclusion: { in: vehicle_types.keys }
validates :plate_number, presence: true, length: { in: 2..9 }
validates :fee, numericality: { greater_than_or_equal_to: 0 }
validates_comparison_of :date_unpark,
greater_than: :date_park,
message: "must be after date park",
if: -> { date_unpark.present? }

def plate_number=(value)
self[:plate_number] = value.upcase
self[:plate_number] = value&.upcase
end

# Overwrite the setter to rely on validations instead of [ArgumentError]
# https://github.com/rails/rails/issues/13971#issuecomment-721821257
def vehicle_type=(value)
self[:vehicle_type] = value
rescue ArgumentError
self[:vehicle_type] = nil
end

def park_vehicle!(slot, params)
Expand Down
3 changes: 1 addition & 2 deletions app/models/entrance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ class Entrance < ApplicationRecord
case_sensitive: false
},
length: {
minimum: 1,
maximum: 15
in: 1..15
},
format: %r{\A[a-zA-Z0-9\s\-/&]*\z}
end
5 changes: 2 additions & 3 deletions app/models/parking_lot.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

class ParkingLot < ApplicationRecord
has_many :parking_slots, dependent: :destroy
has_many :parking_slots, inverse_of: :parking_lot, dependent: :destroy

accepts_nested_attributes_for :parking_slots,
reject_if:
Expand All @@ -16,8 +16,7 @@ class ParkingLot < ApplicationRecord
case_sensitive: false
},
length: {
minimum: 2,
maximum: 15
in: 2..15
},
format: %r{\A[a-zA-Z0-9\s\-/&]*\z}

Expand Down
24 changes: 19 additions & 5 deletions app/models/parking_slot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,31 @@ class ParkingSlot < ApplicationRecord
enum :slot_type, { small: 0, medium: 1, large: 2 }
enum :status, { vacant: 0, reserved: 1, occupied: 2 }, default: :vacant

belongs_to :parking_lot
belongs_to :parking_lot, inverse_of: :parking_slots

has_many :bookings, dependent: :restrict_with_exception
has_many :bookings, inverse_of: :parking_slot, dependent: :restrict_with_exception

validates :slot_type, presence: true, inclusion: { in: slot_types.keys }
validates :status, presence: true, inclusion: { in: statuses.keys }
validates :slot_type, inclusion: { in: slot_types.keys }
validates :status, inclusion: { in: statuses.keys }

validate :validate_distances

before_validation :set_distances

# Overwrite the setter to rely on validations instead of [ArgumentError]
# https://github.com/rails/rails/issues/13971#issuecomment-721821257
def slot_type=(value)
self[:slot_type] = value
rescue ArgumentError
self[:slot_type] = nil
end

def status=(value)
self[:status] = value
rescue ArgumentError
self[:status] = nil
end

def code
id.to_s.rjust(3, "0")
end
Expand All @@ -28,7 +42,7 @@ def distances_arr
def validate_distances
# Check if distances length/size is equal to the number of entrances
return if distances_arr.size == Entrance.count
errors.add(:base, "has invalid distances.")
errors.add(:distances, "are invalid.")
end

def set_distances
Expand Down
5 changes: 5 additions & 0 deletions config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@

Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
config.after_initialize do
Bullet.enable = false # enable Bullet gem, otherwise do nothing
Bullet.bullet_logger = true # log to the Bullet log file (Rails.root/log/bullet.log)
Bullet.raise = true # raise an error if n+1 query occurs
end

# While tests run files are not watched, reloading is not necessary.
config.enable_reloading = false
Expand Down
9 changes: 9 additions & 0 deletions spec/factories/bookings.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

FactoryBot.define do
factory :booking do
parking_slot
vehicle_type { :large }
plate_number { "ABC123" }
end
end
7 changes: 7 additions & 0 deletions spec/factories/entrances.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

FactoryBot.define do
factory :entrance do
sequence(:name) { |n| "Entrance #{n}" }
end
end
11 changes: 11 additions & 0 deletions spec/factories/parking_lots.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

FactoryBot.define do
factory :parking_lot do
sequence(:name) { |n| "PL #{n}" }

transient { slots_count { 2 } }

after(:build) { |parking_lot, evaluator| build_list(:parking_slot, evaluator.slots_count, parking_lot:) }
end
end
10 changes: 10 additions & 0 deletions spec/factories/parking_slots.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

FactoryBot.define do
factory :parking_slot do
parking_lot
slot_type { :large }
status { :vacant }
distances { Array.new(Entrance.count) { |index| index + 1 }.join(",") }
end
end
Loading

0 comments on commit e77ef0a

Please sign in to comment.