97 lines
3.8 KiB
Ruby
97 lines
3.8 KiB
Ruby
require 'rails_helper'
|
|
|
|
RSpec.describe Analytics::OperacaoMetricas do
|
|
# Linhas simuladas (mesma forma do retorno de select_all) — evita depender das
|
|
# tabelas externas no teste. Stubamos #registros para focar nas agregações.
|
|
let(:rows) do
|
|
[
|
|
row(status: 'completed', driver: 'Carlos', sts: 'STS PERUS', gade: 'RECORRENTE', checkout: '2026-06-22 10:00:00', clat: '-23.5', clng: '-46.6'),
|
|
row(status: 'completed', driver: 'Carlos', sts: 'STS PERUS', gade: 'RECORRENTE', checkout: '2026-06-22 11:00:00', lat: '-23.6', lng: '-46.7'),
|
|
row(status: 'completed', driver: 'Pedro', sts: 'STS PIRITUBA', gade: 'RECORRENTE', checkout: '2026-06-23 09:00:00', clat: '-23.4', clng: '-46.5'),
|
|
row(status: 'failed', driver: 'Pedro', sts: 'STS PIRITUBA', gade: 'NOVO', obs: 'ÓBITO', checkout: '2026-06-23 12:00:00'),
|
|
row(status: 'failed', driver: 'Carlos', sts: 'STS PERUS', gade: 'NOVO', obs: 'ÓBITO', checkout: '2026-06-22 13:00:00'),
|
|
row(status: 'pending', driver: 'Marcos', sts: 'STS PERUS', gade: 'NOVO', planned: '2026-06-24')
|
|
]
|
|
end
|
|
|
|
subject(:metricas) do
|
|
described_class.new(tabelas: [], inicio: Date.new(2026, 6, 1), fim: Date.new(2026, 6, 30))
|
|
end
|
|
|
|
before do
|
|
# Isola das tabelas externas: não toca o catálogo nem db_reem_simplerout_2026.
|
|
allow(Operacao).to receive(:sanitizar).and_return([])
|
|
allow(metricas).to receive(:registros).and_return(rows)
|
|
end
|
|
|
|
it 'conta KPIs (total/sucesso/recusas/pendentes)' do
|
|
expect(metricas.total).to eq(6)
|
|
expect(metricas.sucesso).to eq(3)
|
|
expect(metricas.recusas).to eq(2)
|
|
expect(metricas.pendentes).to eq(1)
|
|
end
|
|
|
|
it 'calcula insucessos %' do
|
|
ins = metricas.insucessos_pct
|
|
expect(ins[:completed]).to eq(3)
|
|
expect(ins[:failed]).to eq(2)
|
|
expect(ins[:pct_completed]).to eq(50.0)
|
|
expect(ins[:pct_failed]).to eq(33.33)
|
|
end
|
|
|
|
it 'agrupa índices de falha por observação' do
|
|
expect(metricas.indices_falha).to eq([{ observation: 'ÓBITO', total: 2, pct: 100.0 }])
|
|
end
|
|
|
|
it 'agrupa por status_gade' do
|
|
expect(metricas.por_status_gade).to contain_exactly(
|
|
{ status: 'RECORRENTE', total: 3 },
|
|
{ status: 'NOVO', total: 3 }
|
|
)
|
|
end
|
|
|
|
it 'conta NFs por motorista (todas), desc' do
|
|
expect(metricas.por_motorista.first).to eq({ nome: 'Carlos', total: 3 })
|
|
expect(metricas.por_motorista.map { |h| h[:nome] }).to eq(%w[Carlos Pedro Marcos])
|
|
end
|
|
|
|
it 'conta entregas concluídas por STS (contact_name)' do
|
|
expect(metricas.por_sts).to eq([
|
|
{ nome: 'STS PERUS', total: 2 },
|
|
{ nome: 'STS PIRITUBA', total: 1 }
|
|
])
|
|
end
|
|
|
|
it 'agrupa por dia (completas x falhas)' do
|
|
pd = metricas.por_dia
|
|
expect(pd[:labels]).to eq(['22/06/2026', '23/06/2026'])
|
|
expect(pd[:completed]).to eq([2, 1])
|
|
expect(pd[:failed]).to eq([1, 1])
|
|
end
|
|
|
|
it 'monta pontos do mapa (prefere checkout, cai p/ checkin)' do
|
|
expect(metricas.pontos_mapa).to eq([[-23.5, -46.6], [-23.6, -46.7], [-23.4, -46.5]])
|
|
end
|
|
|
|
context 'sem registros' do
|
|
before { allow(metricas).to receive(:registros).and_return([]) }
|
|
|
|
it 'não divide por zero' do
|
|
expect(metricas.total).to eq(0)
|
|
expect(metricas.insucessos_pct[:pct_failed]).to eq(0.0)
|
|
expect(metricas.indices_falha).to eq([])
|
|
expect(metricas.pontos_mapa).to eq([])
|
|
expect(metricas.centro_mapa).to eq(described_class::SP_CENTRO)
|
|
end
|
|
end
|
|
|
|
def row(status:, driver:, sts:, gade:, obs: nil, checkout: nil, planned: nil, lat: nil, lng: nil, clat: nil, clng: nil)
|
|
{
|
|
'status' => status, 'driver' => driver, 'contact_name' => sts, 'status_gade' => gade,
|
|
'observation' => obs, 'checkout' => checkout, 'planned_date' => planned,
|
|
'latitude' => lat, 'longitude' => lng,
|
|
'checkout_latitude' => clat, 'checkout_longitude' => clng
|
|
}
|
|
end
|
|
end
|