Finished phase006
This commit is contained in:
@@ -4,5 +4,8 @@ FactoryBot.define do
|
||||
maintenance_type { "Oil Change" }
|
||||
mileage_interval { 5_000 }
|
||||
time_interval { 180 }
|
||||
baseline_odometer { 10_000 }
|
||||
baseline_date { Date.current }
|
||||
active { true }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,18 +6,43 @@ RSpec.describe MaintenanceSchedule do
|
||||
end
|
||||
|
||||
it "requires all fields" do
|
||||
schedule = build(:maintenance_schedule, vehicle: nil, maintenance_type: nil, mileage_interval: nil, time_interval: nil)
|
||||
schedule = build(
|
||||
:maintenance_schedule,
|
||||
vehicle: nil,
|
||||
maintenance_type: nil,
|
||||
mileage_interval: nil,
|
||||
time_interval: nil,
|
||||
baseline_odometer: nil,
|
||||
baseline_date: nil,
|
||||
active: nil
|
||||
)
|
||||
|
||||
expect(schedule).not_to be_valid
|
||||
expect(schedule.errors.attribute_names).to include(:vehicle, :maintenance_type, :mileage_interval, :time_interval)
|
||||
expect(schedule.errors.attribute_names).to include(
|
||||
:vehicle,
|
||||
:maintenance_type,
|
||||
:mileage_interval,
|
||||
:time_interval,
|
||||
:baseline_odometer,
|
||||
:baseline_date,
|
||||
:active
|
||||
)
|
||||
end
|
||||
|
||||
it "requires positive integer intervals" do
|
||||
schedule = build(:maintenance_schedule, mileage_interval: 0, time_interval: 0)
|
||||
it "requires valid numeric values" do
|
||||
schedule = build(:maintenance_schedule, mileage_interval: 0, time_interval: 0, baseline_odometer: -1)
|
||||
|
||||
expect(schedule).not_to be_valid
|
||||
expect(schedule.errors[:mileage_interval]).to be_present
|
||||
expect(schedule.errors[:time_interval]).to be_present
|
||||
expect(schedule.errors[:baseline_odometer]).to be_present
|
||||
end
|
||||
|
||||
it "rejects future baseline dates" do
|
||||
schedule = build(:maintenance_schedule, baseline_date: Date.current + 1.day)
|
||||
|
||||
expect(schedule).not_to be_valid
|
||||
expect(schedule.errors[:baseline_date]).to be_present
|
||||
end
|
||||
|
||||
it "requires maintenance type to be unique per vehicle case-insensitively" do
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe MaintenanceScheduleStatus do
|
||||
it "marks a schedule overdue when the mileage interval is reached" do
|
||||
schedule = build(:maintenance_schedule, mileage_interval: 5_000, baseline_odometer: 10_000, time_interval: 180)
|
||||
|
||||
status = described_class.new(schedule, nil, current_odometer: 15_000)
|
||||
|
||||
expect(status.status).to eq("overdue")
|
||||
expect(status.reason).to include("mileage interval reached")
|
||||
end
|
||||
|
||||
it "marks a schedule overdue when the time interval is reached" do
|
||||
schedule = build(:maintenance_schedule, time_interval: 180, baseline_date: Date.current - 180.days)
|
||||
|
||||
status = described_class.new(schedule, nil, current_odometer: 10_000)
|
||||
|
||||
expect(status.status).to eq("overdue")
|
||||
expect(status.reason).to include("time interval reached")
|
||||
end
|
||||
|
||||
it "marks a schedule upcoming within five percent of the mileage interval" do
|
||||
schedule = build(:maintenance_schedule, mileage_interval: 5_000, baseline_odometer: 10_000, baseline_date: Date.current)
|
||||
|
||||
status = described_class.new(schedule, nil, current_odometer: 14_750)
|
||||
|
||||
expect(status.status).to eq("upcoming")
|
||||
expect(status.reason).to include("within 250 miles")
|
||||
end
|
||||
|
||||
it "marks a schedule upcoming within five percent of the time interval" do
|
||||
schedule = build(:maintenance_schedule, time_interval: 180, baseline_date: Date.current - 171.days)
|
||||
|
||||
status = described_class.new(schedule, nil, current_odometer: 10_000)
|
||||
|
||||
expect(status.status).to eq("upcoming")
|
||||
expect(status.reason).to include("within 9 days")
|
||||
end
|
||||
|
||||
it "marks a no-history schedule as no history when it is not upcoming or overdue" do
|
||||
schedule = build(:maintenance_schedule, baseline_odometer: 10_000, mileage_interval: 5_000, baseline_date: Date.current)
|
||||
|
||||
status = described_class.new(schedule, nil, current_odometer: 10_100)
|
||||
|
||||
expect(status.status).to eq("no_history")
|
||||
end
|
||||
|
||||
it "marks a schedule current when matching history exists and no interval is near" do
|
||||
schedule = build(:maintenance_schedule, mileage_interval: 5_000, time_interval: 180)
|
||||
entry = build(:maintenance_entry, odometer: 12_000, date: Date.current - 10.days)
|
||||
|
||||
status = described_class.new(schedule, entry, current_odometer: 12_500)
|
||||
|
||||
expect(status.status).to eq("current")
|
||||
expect(status.due_odometer).to eq(17_000)
|
||||
expect(status.due_date).to eq(entry.date + 180.days)
|
||||
end
|
||||
end
|
||||
@@ -111,6 +111,7 @@ RSpec.describe "Vehicles" do
|
||||
expect(response.body).to include(vehicle.make)
|
||||
expect(response.body).to include(vehicle.model)
|
||||
expect(response.body).to include("Maintenance Schedules")
|
||||
expect(response.body).to include("Manage maintenance schedules")
|
||||
expect(response.body).to include("Fuel History")
|
||||
expect(response.body).to include("Maintenance History")
|
||||
expect(response.body).to include(vehicle.maintenance_schedules.first.maintenance_type)
|
||||
@@ -121,6 +122,141 @@ RSpec.describe "Vehicles" do
|
||||
expect(response.body.index("12,000")).to be < response.body.index("11,000")
|
||||
expect(response.body.index(newer_maintenance_entry.notes)).to be < response.body.index(older_maintenance_entry.notes)
|
||||
end
|
||||
|
||||
it "shows overdue maintenance alerts" do
|
||||
vehicle = create(:vehicle, current_odometer: 15_000)
|
||||
create(
|
||||
:maintenance_schedule,
|
||||
vehicle: vehicle,
|
||||
maintenance_type: "Oil Change",
|
||||
mileage_interval: 5_000,
|
||||
baseline_odometer: 10_000,
|
||||
baseline_date: Date.current
|
||||
)
|
||||
|
||||
get vehicle_path(vehicle)
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include("Overdue Maintenance")
|
||||
expect(response.body).to include("Oil Change is overdue")
|
||||
end
|
||||
|
||||
it "shows upcoming maintenance warnings" do
|
||||
vehicle = create(:vehicle, current_odometer: 14_750)
|
||||
create(
|
||||
:maintenance_schedule,
|
||||
vehicle: vehicle,
|
||||
maintenance_type: "Oil Change",
|
||||
mileage_interval: 5_000,
|
||||
baseline_odometer: 10_000,
|
||||
baseline_date: Date.current
|
||||
)
|
||||
|
||||
get vehicle_path(vehicle)
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include("Upcoming Maintenance")
|
||||
expect(response.body).to include("Oil Change is upcoming")
|
||||
end
|
||||
|
||||
it "shows no-history maintenance status without marking it overdue" do
|
||||
vehicle = create(:vehicle, current_odometer: 10_100)
|
||||
create(
|
||||
:maintenance_schedule,
|
||||
vehicle: vehicle,
|
||||
maintenance_type: "Oil Change",
|
||||
mileage_interval: 5_000,
|
||||
baseline_odometer: 10_000,
|
||||
baseline_date: Date.current
|
||||
)
|
||||
|
||||
get vehicle_path(vehicle)
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include("No Maintenance History")
|
||||
expect(response.body).to include("Oil Change has no matching maintenance entry yet")
|
||||
expect(response.body).not_to include("Oil Change is overdue")
|
||||
end
|
||||
|
||||
it "logs overdue notifications only when schedule status changes" do
|
||||
vehicle = create(:vehicle, current_odometer: 15_000)
|
||||
schedule = create(
|
||||
:maintenance_schedule,
|
||||
vehicle: vehicle,
|
||||
maintenance_type: "Oil Change",
|
||||
mileage_interval: 5_000,
|
||||
baseline_odometer: 10_000,
|
||||
baseline_date: Date.current
|
||||
)
|
||||
allow(Rails.logger).to receive(:warn)
|
||||
|
||||
get vehicle_path(vehicle)
|
||||
get vehicle_path(vehicle)
|
||||
|
||||
expect(schedule.reload.last_status).to eq("overdue")
|
||||
expect(Rails.logger).to have_received(:warn).once.with(/Maintenance schedule overdue: vehicle_id=#{vehicle.id}/)
|
||||
end
|
||||
end
|
||||
|
||||
describe "maintenance schedule CRUD" do
|
||||
before { identify_user }
|
||||
|
||||
it "lists maintenance schedules" do
|
||||
vehicle = create(:vehicle)
|
||||
create(:maintenance_schedule, vehicle: vehicle, maintenance_type: "Oil Change")
|
||||
|
||||
get vehicle_maintenance_schedules_path(vehicle)
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include("Maintenance Schedules")
|
||||
expect(response.body).to include("Oil Change")
|
||||
end
|
||||
|
||||
it "creates a maintenance schedule" do
|
||||
vehicle = create(:vehicle)
|
||||
|
||||
expect do
|
||||
post vehicle_maintenance_schedules_path(vehicle), params: { maintenance_schedule: valid_maintenance_schedule_params }
|
||||
end.to change(MaintenanceSchedule, :count).by(1)
|
||||
|
||||
expect(response).to redirect_to(vehicle_maintenance_schedules_path(vehicle))
|
||||
expect(MaintenanceSchedule.last.baseline_odometer).to eq(10_000)
|
||||
end
|
||||
|
||||
it "rejects invalid maintenance schedule params" do
|
||||
vehicle = create(:vehicle)
|
||||
|
||||
expect do
|
||||
post vehicle_maintenance_schedules_path(vehicle), params: { maintenance_schedule: valid_maintenance_schedule_params.merge(mileage_interval: 0) }
|
||||
end.not_to change(MaintenanceSchedule, :count)
|
||||
|
||||
expect(response).to have_http_status(:unprocessable_content)
|
||||
expect(response.body).to include("prevented this maintenance schedule from being saved")
|
||||
end
|
||||
|
||||
it "updates a maintenance schedule" do
|
||||
vehicle = create(:vehicle)
|
||||
schedule = create(:maintenance_schedule, vehicle: vehicle, maintenance_type: "Oil Change")
|
||||
|
||||
patch vehicle_maintenance_schedule_path(vehicle, schedule), params: {
|
||||
maintenance_schedule: valid_maintenance_schedule_params.merge(maintenance_type: "Tire Rotation")
|
||||
}
|
||||
|
||||
expect(response).to redirect_to(vehicle_maintenance_schedules_path(vehicle))
|
||||
expect(schedule.reload.maintenance_type).to eq("Tire Rotation")
|
||||
end
|
||||
|
||||
it "soft deactivates a maintenance schedule" do
|
||||
vehicle = create(:vehicle)
|
||||
schedule = create(:maintenance_schedule, vehicle: vehicle, active: true)
|
||||
|
||||
expect do
|
||||
patch deactivate_vehicle_maintenance_schedule_path(vehicle, schedule)
|
||||
end.not_to change(MaintenanceSchedule, :count)
|
||||
|
||||
expect(response).to redirect_to(vehicle_maintenance_schedules_path(vehicle))
|
||||
expect(schedule.reload).not_to be_active
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /vehicles/:vehicle_id/fuel_entries" do
|
||||
@@ -368,6 +504,17 @@ RSpec.describe "Vehicles" do
|
||||
}
|
||||
end
|
||||
|
||||
def valid_maintenance_schedule_params
|
||||
{
|
||||
maintenance_type: "Oil Change",
|
||||
mileage_interval: 5_000,
|
||||
time_interval: 180,
|
||||
baseline_odometer: 10_000,
|
||||
baseline_date: Date.current,
|
||||
active: "1"
|
||||
}
|
||||
end
|
||||
|
||||
def identify_user(user = create(:user))
|
||||
@identified_user = user
|
||||
access_key = AccessKey.current
|
||||
|
||||
Reference in New Issue
Block a user