From a387dc195a54ce3b80946768ca8fd60cd072a2a2 Mon Sep 17 00:00:00 2001 From: william-virissimo Date: Mon, 15 Dec 2025 01:44:02 -0300 Subject: [PATCH 1/6] =?UTF-8?q?teste=20das=20controllers=20de=20modelo=20e?= =?UTF-8?q?=20avalia=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile.lock | 8 ++ test/controllers/avaliacao_controller_test.rb | 84 +++++++++++++ test/controllers/modelos_controller_test.rb | 113 ++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 test/controllers/avaliacao_controller_test.rb create mode 100644 test/controllers/modelos_controller_test.rb diff --git a/Gemfile.lock b/Gemfile.lock index 666370383b..e6a6f37c3e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -190,6 +190,7 @@ GEM ffi (1.17.2-aarch64-linux-musl) ffi (1.17.2-arm-linux-gnu) ffi (1.17.2-arm-linux-musl) + ffi (1.17.2-x64-mingw-ucrt) ffi (1.17.2-x86_64-linux-gnu) ffi (1.17.2-x86_64-linux-musl) flay (2.13.3) @@ -281,6 +282,8 @@ GEM racc (~> 1.4) nokogiri (1.18.10-arm-linux-musl) racc (~> 1.4) + nokogiri (1.18.10-x64-mingw-ucrt) + racc (~> 1.4) nokogiri (1.18.10-x86_64-linux-gnu) racc (~> 1.4) nokogiri (1.18.10-x86_64-linux-musl) @@ -457,6 +460,7 @@ GEM sqlite3 (2.8.0-aarch64-linux-musl) sqlite3 (2.8.0-arm-linux-gnu) sqlite3 (2.8.0-arm-linux-musl) + sqlite3 (2.8.0-x64-mingw-ucrt) sqlite3 (2.8.0-x86_64-linux-gnu) sqlite3 (2.8.0-x86_64-linux-musl) sshkit (1.24.0) @@ -478,6 +482,7 @@ GEM tailwindcss-ruby (4.1.16) tailwindcss-ruby (4.1.16-aarch64-linux-gnu) tailwindcss-ruby (4.1.16-aarch64-linux-musl) + tailwindcss-ruby (4.1.16-x64-mingw-ucrt) tailwindcss-ruby (4.1.16-x86_64-linux-gnu) tailwindcss-ruby (4.1.16-x86_64-linux-musl) thor (1.4.0) @@ -493,6 +498,8 @@ GEM railties (>= 7.1.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) + tzinfo-data (1.2025.3) + tzinfo (>= 1.0.0) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) unicode-emoji (4.1.0) @@ -522,6 +529,7 @@ PLATFORMS aarch64-linux-musl arm-linux-gnu arm-linux-musl + x64-mingw-ucrt x86_64-linux x86_64-linux-gnu x86_64-linux-musl diff --git a/test/controllers/avaliacao_controller_test.rb b/test/controllers/avaliacao_controller_test.rb new file mode 100644 index 0000000000..c84d4e787a --- /dev/null +++ b/test/controllers/avaliacao_controller_test.rb @@ -0,0 +1,84 @@ +# spec/controllers/avaliacoes_controller_spec.rb +require 'rails_helper' + +RSpec.describe AvaliacoesController, type: :controller do + let(:admin) { create(:user, eh_admin: true) } + let(:aluno) { create(:user, eh_admin: false) } + let(:turma) { create(:turma) } + let(:modelo) { create(:modelo, titulo: "Template Padrão", ativo: true) } + let(:avaliacao) { create(:avaliacao, turma: turma, modelo: modelo) } + + def login(user) + allow(controller).to receive(:current_user).and_return(user) + end + + describe 'GET #index' do + it 'admin sees all avaliacoes' do + login(admin) + get :index + expect(assigns(:avaliacoes)).to be_present + end + + it 'student sees their turmas' do + login(aluno) + aluno.turmas << turma + get :index + expect(assigns(:turmas)).to include(turma) + end + + it 'redirects when not logged in' do + login(nil) + get :index + expect(response).to redirect_to(new_session_path) + end + end + + describe 'GET #gestao_envios' do + it 'loads turmas' do + login(admin) + get :gestao_envios + expect(assigns(:turmas)).to be_present + end + end + + describe 'POST #create' do + before { login(admin) } + + it 'creates avaliacao with valid data' do + modelo + expect { + post :create, params: { turma_id: turma.id } + }.to change(Avaliacao, :count).by(1) + end + + it 'fails when turma not found' do + post :create, params: { turma_id: 99999 } + expect(flash[:alert]).to eq('Turma não encontrada.') + end + + it 'fails when template not found' do + post :create, params: { turma_id: turma.id } + expect(flash[:alert]).to include('Template Padrão não encontrado') + end + end + + describe 'GET #resultados' do + before { login(admin) } + + it 'shows results page' do + get :resultados, params: { id: avaliacao.id } + expect(response).to be_successful + end + + it 'generates CSV' do + get :resultados, params: { id: avaliacao.id }, format: :csv + expect(response.content_type).to eq('text/csv') + end + + it 'handles errors gracefully' do + allow_any_instance_of(Avaliacao).to receive(:submissoes).and_raise(ActiveRecord::StatementInvalid) + get :resultados, params: { id: avaliacao.id } + expect(assigns(:submissoes)).to eq([]) + end + end +end \ No newline at end of file diff --git a/test/controllers/modelos_controller_test.rb b/test/controllers/modelos_controller_test.rb new file mode 100644 index 0000000000..a00c141d48 --- /dev/null +++ b/test/controllers/modelos_controller_test.rb @@ -0,0 +1,113 @@ +# spec/controllers/modelos_controller_spec.rb +require 'rails_helper' + +RSpec.describe ModelosController, type: :controller do + let(:admin_user) { create(:user, eh_admin: true) } + let(:regular_user) { create(:user, eh_admin: false) } + let(:modelo) { create(:modelo) } + let(:valid_params) { { titulo: 'Modelo Teste', ativo: true } } + let(:invalid_params) { { titulo: '', ativo: true } } + + def login_as(user) + session = create(:session, user: user) + allow(Current).to receive(:session).and_return(session) + end + + describe 'Authorization' do + it 'blocks non-admin users' do + login_as(regular_user) + get :index + expect(response).to redirect_to(root_path) + end + + it 'allows admin users' do + login_as(admin_user) + get :index + expect(response).to be_successful + end + end + + before { login_as(admin_user) } + + describe 'GET #index' do + it 'lists all modelos' do + get :index + expect(response).to be_successful + expect(assigns(:modelos)).to be_present + end + end + + describe 'GET #show' do + it 'shows a modelo' do + get :show, params: { id: modelo.id } + expect(response).to be_successful + expect(assigns(:modelo)).to eq(modelo) + end + end + + describe 'GET #new' do + it 'creates blank modelo with 3 perguntas' do + get :new + expect(assigns(:modelo).perguntas.size).to eq(3) + end + end + + describe 'GET #edit' do + it 'loads modelo for editing' do + get :edit, params: { id: modelo.id } + expect(response).to be_successful + end + end + + describe 'POST #create' do + it 'creates modelo with valid data' do + expect { + post :create, params: { modelo: valid_params } + }.to change(Modelo, :count).by(1) + expect(response).to redirect_to(Modelo.last) + end + + it 'fails with invalid data' do + post :create, params: { modelo: invalid_params } + expect(response).to render_template(:new) + end + end + + describe 'PATCH #update' do + it 'updates modelo with valid data' do + patch :update, params: { id: modelo.id, modelo: { titulo: 'Novo Título' } } + modelo.reload + expect(modelo.titulo).to eq('Novo Título') + end + + it 'fails with invalid data' do + patch :update, params: { id: modelo.id, modelo: invalid_params } + expect(response).to render_template(:edit) + end + end + + describe 'DELETE #destroy' do + it 'deletes when not in use' do + allow_any_instance_of(Modelo).to receive(:em_uso?).and_return(false) + modelo_to_delete = create(:modelo) + expect { + delete :destroy, params: { id: modelo_to_delete.id } + }.to change(Modelo, :count).by(-1) + end + + it 'blocks deletion when in use' do + allow_any_instance_of(Modelo).to receive(:em_uso?).and_return(true) + delete :destroy, params: { id: modelo.id } + expect(flash[:alert]).to be_present + end + end + + describe 'POST #clone' do + it 'clones modelo successfully' do + expect { + post :clone, params: { id: modelo.id } + }.to change(Modelo, :count).by(1) + expect(response).to redirect_to(edit_modelo_path(Modelo.last)) + end + end +end \ No newline at end of file From 8fdcb36e3a299e0fac3f64277e305e68a21b9ee4 Mon Sep 17 00:00:00 2001 From: william-virissimo Date: Mon, 15 Dec 2025 12:21:33 -0300 Subject: [PATCH 2/6] arquivos spec das controllers avaliacao e modelos --- Gemfile.lock | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e6a6f37c3e..666370383b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -190,7 +190,6 @@ GEM ffi (1.17.2-aarch64-linux-musl) ffi (1.17.2-arm-linux-gnu) ffi (1.17.2-arm-linux-musl) - ffi (1.17.2-x64-mingw-ucrt) ffi (1.17.2-x86_64-linux-gnu) ffi (1.17.2-x86_64-linux-musl) flay (2.13.3) @@ -282,8 +281,6 @@ GEM racc (~> 1.4) nokogiri (1.18.10-arm-linux-musl) racc (~> 1.4) - nokogiri (1.18.10-x64-mingw-ucrt) - racc (~> 1.4) nokogiri (1.18.10-x86_64-linux-gnu) racc (~> 1.4) nokogiri (1.18.10-x86_64-linux-musl) @@ -460,7 +457,6 @@ GEM sqlite3 (2.8.0-aarch64-linux-musl) sqlite3 (2.8.0-arm-linux-gnu) sqlite3 (2.8.0-arm-linux-musl) - sqlite3 (2.8.0-x64-mingw-ucrt) sqlite3 (2.8.0-x86_64-linux-gnu) sqlite3 (2.8.0-x86_64-linux-musl) sshkit (1.24.0) @@ -482,7 +478,6 @@ GEM tailwindcss-ruby (4.1.16) tailwindcss-ruby (4.1.16-aarch64-linux-gnu) tailwindcss-ruby (4.1.16-aarch64-linux-musl) - tailwindcss-ruby (4.1.16-x64-mingw-ucrt) tailwindcss-ruby (4.1.16-x86_64-linux-gnu) tailwindcss-ruby (4.1.16-x86_64-linux-musl) thor (1.4.0) @@ -498,8 +493,6 @@ GEM railties (>= 7.1.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - tzinfo-data (1.2025.3) - tzinfo (>= 1.0.0) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) unicode-emoji (4.1.0) @@ -529,7 +522,6 @@ PLATFORMS aarch64-linux-musl arm-linux-gnu arm-linux-musl - x64-mingw-ucrt x86_64-linux x86_64-linux-gnu x86_64-linux-musl From f7e4eb22dca28449d73951eaf0fbf40a78e94e1c Mon Sep 17 00:00:00 2001 From: william-virissimo Date: Mon, 15 Dec 2025 12:27:14 -0300 Subject: [PATCH 3/6] arquivos spec das controllers avaliacao e modelos --- .../controllers/concerns/avaliacao_controller_spec.rb | 0 .../controllers/concerns/modelos_controller_spec.rb | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename test/controllers/avaliacao_controller_test.rb => spec/controllers/concerns/avaliacao_controller_spec.rb (100%) rename test/controllers/modelos_controller_test.rb => spec/controllers/concerns/modelos_controller_spec.rb (100%) diff --git a/test/controllers/avaliacao_controller_test.rb b/spec/controllers/concerns/avaliacao_controller_spec.rb similarity index 100% rename from test/controllers/avaliacao_controller_test.rb rename to spec/controllers/concerns/avaliacao_controller_spec.rb diff --git a/test/controllers/modelos_controller_test.rb b/spec/controllers/concerns/modelos_controller_spec.rb similarity index 100% rename from test/controllers/modelos_controller_test.rb rename to spec/controllers/concerns/modelos_controller_spec.rb From d6adb635732d4465038395c3e1538220f7dc3043 Mon Sep 17 00:00:00 2001 From: william-virissimo Date: Mon, 15 Dec 2025 12:31:20 -0300 Subject: [PATCH 4/6] aquivos spec formatado no padrao do projeto das controllers avaliacao e modelos --- spec/controllers/concerns/avaliacao_controller_spec.rb | 2 +- spec/controllers/concerns/modelos_controller_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/concerns/avaliacao_controller_spec.rb b/spec/controllers/concerns/avaliacao_controller_spec.rb index c84d4e787a..e3261a5511 100644 --- a/spec/controllers/concerns/avaliacao_controller_spec.rb +++ b/spec/controllers/concerns/avaliacao_controller_spec.rb @@ -81,4 +81,4 @@ def login(user) expect(assigns(:submissoes)).to eq([]) end end -end \ No newline at end of file +end diff --git a/spec/controllers/concerns/modelos_controller_spec.rb b/spec/controllers/concerns/modelos_controller_spec.rb index a00c141d48..3ef1210525 100644 --- a/spec/controllers/concerns/modelos_controller_spec.rb +++ b/spec/controllers/concerns/modelos_controller_spec.rb @@ -110,4 +110,4 @@ def login_as(user) expect(response).to redirect_to(edit_modelo_path(Modelo.last)) end end -end \ No newline at end of file +end From 6848495763c4ca74f2360e0635b436062d568729 Mon Sep 17 00:00:00 2001 From: william-virissimo Date: Tue, 16 Dec 2025 01:04:15 +0000 Subject: [PATCH 5/6] corrige test rspec avaliacao e modelos --- spec/controllers/avaliacao_controller_spec.rb | 203 +++++++++++++++ .../concerns/avaliacao_controller_spec.rb | 84 ------- .../concerns/modelos_controller_spec.rb | 113 --------- spec/controllers/modelos_controller_spec.rb | 231 ++++++++++++++++++ 4 files changed, 434 insertions(+), 197 deletions(-) create mode 100644 spec/controllers/avaliacao_controller_spec.rb delete mode 100644 spec/controllers/concerns/avaliacao_controller_spec.rb delete mode 100644 spec/controllers/concerns/modelos_controller_spec.rb create mode 100644 spec/controllers/modelos_controller_spec.rb diff --git a/spec/controllers/avaliacao_controller_spec.rb b/spec/controllers/avaliacao_controller_spec.rb new file mode 100644 index 0000000000..f5f2485de5 --- /dev/null +++ b/spec/controllers/avaliacao_controller_spec.rb @@ -0,0 +1,203 @@ +require 'rails_helper' + +RSpec.describe AvaliacoesController, type: :controller do + + let(:valid_question_type) { "texto_curto" } + + def create_turma + Turma.create!( + codigo: "ENG-#{rand(100..999)}", + nome: "Engenharia de Software", + semestre: "2025/1" + ) + end + + def create_template_padrao + modelo = Modelo.new(titulo: "Template Padrão", ativo: true) + modelo.perguntas.build(enunciado: "Pergunta Padrão", tipo: valid_question_type, opcoes: []) + modelo.save! + modelo + end + + def create_modelo_generico + modelo = Modelo.new(titulo: "Outro Modelo", ativo: true) + modelo.perguntas.build(enunciado: "Questão 1", tipo: valid_question_type, opcoes: []) + modelo.save! + modelo + end + + def create_avaliacao(turma, modelo) + Avaliacao.create!( + turma: turma, + modelo: modelo, + data_inicio: Time.current, + data_fim: 7.days.from_now + ) + end + + def stub_current_user(admin: false, turmas: []) + user = double("User", eh_admin?: admin, id: 1) + + allow(controller).to receive(:current_user).and_return(user) + + session_double = double("Session", user: user) + allow(Current).to receive(:session).and_return(session_double) + allow(Current).to receive(:user).and_return(user) + + unless admin + ids = turmas.map(&:id) + relation = Turma.where(id: ids) + allow(user).to receive(:turmas).and_return(relation) + end + + user + end + + + describe "GET #index" do + context "quando o usuário NÃO está logado" do + before do + allow(controller).to receive(:current_user).and_return(nil) + allow(Current).to receive(:session).and_return(nil) + end + + it "redireciona para o login" do + get :index + expect(response).to redirect_to(new_session_path) + end + end + + context "quando o usuário é ADMIN" do + before { stub_current_user(admin: true) } + + it "retorna sucesso (200 OK)" do + get :index + expect(response).to be_successful + end + end + + context "quando o usuário é ALUNO" do + let(:turma) { create_turma } + + before do + stub_current_user(admin: false, turmas: [turma]) + end + + it "retorna sucesso (200 OK)" do + get :index + expect(response).to be_successful + end + end + end + + describe "GET #gestao_envios" do + before { stub_current_user(admin: true) } + + it "retorna sucesso e carrega a página" do + get :gestao_envios + expect(response).to be_successful + end + end + + describe "POST #create" do + before { stub_current_user(admin: true) } + let!(:turma) { create_turma } + + context "Cenários de Falha" do + it "redireciona com alerta se a Turma não for encontrada" do + post :create, params: { turma_id: 0 } + expect(response).to redirect_to(gestao_envios_avaliacoes_path) + expect(flash[:alert]).to eq("Turma não encontrada.") + end + + it "redireciona com alerta se o Template Padrão não existir" do + Modelo.where(titulo: "Template Padrão").destroy_all + create_modelo_generico + + post :create, params: { turma_id: turma.id } + expect(response).to redirect_to(gestao_envios_avaliacoes_path) + expect(flash[:alert]).to include("Template Padrão não encontrado") + end + end + + context "Cenário de Sucesso" do + before { create_template_padrao } + + it "cria uma nova avaliação no banco" do + expect { + post :create, params: { turma_id: turma.id, data_fim: 5.days.from_now } + }.to change(Avaliacao, :count).by(1) + end + + it "redireciona com mensagem de sucesso" do + post :create, params: { turma_id: turma.id } + expect(response).to redirect_to(gestao_envios_avaliacoes_path) + expect(flash[:notice]).to include("Avaliação criada com sucesso") + end + + it "define data_fim padrão se não fornecida" do + post :create, params: { turma_id: turma.id, data_fim: "" } + avaliacao = Avaliacao.last + expect(avaliacao.data_fim).to be_within(1.second).of(7.days.from_now) + end + end + + context "Erro de Persistência" do + before { create_template_padrao } + + it "redireciona com alerta se o save falhar" do + allow_any_instance_of(Avaliacao).to receive(:save).and_return(false) + allow_any_instance_of(Avaliacao).to receive_message_chain(:errors, :full_messages).and_return(["Erro no Banco"]) + + post :create, params: { turma_id: turma.id } + expect(response).to redirect_to(gestao_envios_avaliacoes_path) + expect(flash[:alert]).to include("Erro no Banco") + end + end + end + + describe "GET #resultados" do + before { stub_current_user(admin: true) } + + let!(:turma) { create_turma } + let!(:modelo) { create_modelo_generico } + let!(:avaliacao) { create_avaliacao(turma, modelo) } + + context "Formato HTML" do + it "responde com sucesso (200 OK)" do + get :resultados, params: { id: avaliacao.id } + expect(response).to be_successful + end + + it "lida com erro de banco de dados (StatementInvalid) sem quebrar" do + allow(Avaliacao).to receive(:find).with(avaliacao.id.to_s).and_return(avaliacao) + allow(avaliacao).to receive(:submissoes).and_raise(ActiveRecord::StatementInvalid) + + get :resultados, params: { id: avaliacao.id } + + expect(flash[:alert]).to eq("Erro ao carregar submissões.") + expect(response).to be_successful + end + + it "executa a lógica de estatísticas sem erro (Happy Path)" do + get :resultados, params: { id: avaliacao.id } + expect(response).to be_successful + end + end + + context "Formato CSV" do + it "envia o arquivo gerado pelo service" do + csv_dummy_content = "Questao,Resposta\n1,Teste" + service_double = instance_double("CsvFormatterService") + + expect(CsvFormatterService).to receive(:new).with(avaliacao).and_return(service_double) + expect(service_double).to receive(:generate).and_return(csv_dummy_content) + + get :resultados, params: { id: avaliacao.id }, format: :csv + + expect(response.header['Content-Type']).to include('text/csv') + expect(response.body).to eq(csv_dummy_content) + end + end + end +end \ No newline at end of file diff --git a/spec/controllers/concerns/avaliacao_controller_spec.rb b/spec/controllers/concerns/avaliacao_controller_spec.rb deleted file mode 100644 index e3261a5511..0000000000 --- a/spec/controllers/concerns/avaliacao_controller_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -# spec/controllers/avaliacoes_controller_spec.rb -require 'rails_helper' - -RSpec.describe AvaliacoesController, type: :controller do - let(:admin) { create(:user, eh_admin: true) } - let(:aluno) { create(:user, eh_admin: false) } - let(:turma) { create(:turma) } - let(:modelo) { create(:modelo, titulo: "Template Padrão", ativo: true) } - let(:avaliacao) { create(:avaliacao, turma: turma, modelo: modelo) } - - def login(user) - allow(controller).to receive(:current_user).and_return(user) - end - - describe 'GET #index' do - it 'admin sees all avaliacoes' do - login(admin) - get :index - expect(assigns(:avaliacoes)).to be_present - end - - it 'student sees their turmas' do - login(aluno) - aluno.turmas << turma - get :index - expect(assigns(:turmas)).to include(turma) - end - - it 'redirects when not logged in' do - login(nil) - get :index - expect(response).to redirect_to(new_session_path) - end - end - - describe 'GET #gestao_envios' do - it 'loads turmas' do - login(admin) - get :gestao_envios - expect(assigns(:turmas)).to be_present - end - end - - describe 'POST #create' do - before { login(admin) } - - it 'creates avaliacao with valid data' do - modelo - expect { - post :create, params: { turma_id: turma.id } - }.to change(Avaliacao, :count).by(1) - end - - it 'fails when turma not found' do - post :create, params: { turma_id: 99999 } - expect(flash[:alert]).to eq('Turma não encontrada.') - end - - it 'fails when template not found' do - post :create, params: { turma_id: turma.id } - expect(flash[:alert]).to include('Template Padrão não encontrado') - end - end - - describe 'GET #resultados' do - before { login(admin) } - - it 'shows results page' do - get :resultados, params: { id: avaliacao.id } - expect(response).to be_successful - end - - it 'generates CSV' do - get :resultados, params: { id: avaliacao.id }, format: :csv - expect(response.content_type).to eq('text/csv') - end - - it 'handles errors gracefully' do - allow_any_instance_of(Avaliacao).to receive(:submissoes).and_raise(ActiveRecord::StatementInvalid) - get :resultados, params: { id: avaliacao.id } - expect(assigns(:submissoes)).to eq([]) - end - end -end diff --git a/spec/controllers/concerns/modelos_controller_spec.rb b/spec/controllers/concerns/modelos_controller_spec.rb deleted file mode 100644 index 3ef1210525..0000000000 --- a/spec/controllers/concerns/modelos_controller_spec.rb +++ /dev/null @@ -1,113 +0,0 @@ -# spec/controllers/modelos_controller_spec.rb -require 'rails_helper' - -RSpec.describe ModelosController, type: :controller do - let(:admin_user) { create(:user, eh_admin: true) } - let(:regular_user) { create(:user, eh_admin: false) } - let(:modelo) { create(:modelo) } - let(:valid_params) { { titulo: 'Modelo Teste', ativo: true } } - let(:invalid_params) { { titulo: '', ativo: true } } - - def login_as(user) - session = create(:session, user: user) - allow(Current).to receive(:session).and_return(session) - end - - describe 'Authorization' do - it 'blocks non-admin users' do - login_as(regular_user) - get :index - expect(response).to redirect_to(root_path) - end - - it 'allows admin users' do - login_as(admin_user) - get :index - expect(response).to be_successful - end - end - - before { login_as(admin_user) } - - describe 'GET #index' do - it 'lists all modelos' do - get :index - expect(response).to be_successful - expect(assigns(:modelos)).to be_present - end - end - - describe 'GET #show' do - it 'shows a modelo' do - get :show, params: { id: modelo.id } - expect(response).to be_successful - expect(assigns(:modelo)).to eq(modelo) - end - end - - describe 'GET #new' do - it 'creates blank modelo with 3 perguntas' do - get :new - expect(assigns(:modelo).perguntas.size).to eq(3) - end - end - - describe 'GET #edit' do - it 'loads modelo for editing' do - get :edit, params: { id: modelo.id } - expect(response).to be_successful - end - end - - describe 'POST #create' do - it 'creates modelo with valid data' do - expect { - post :create, params: { modelo: valid_params } - }.to change(Modelo, :count).by(1) - expect(response).to redirect_to(Modelo.last) - end - - it 'fails with invalid data' do - post :create, params: { modelo: invalid_params } - expect(response).to render_template(:new) - end - end - - describe 'PATCH #update' do - it 'updates modelo with valid data' do - patch :update, params: { id: modelo.id, modelo: { titulo: 'Novo Título' } } - modelo.reload - expect(modelo.titulo).to eq('Novo Título') - end - - it 'fails with invalid data' do - patch :update, params: { id: modelo.id, modelo: invalid_params } - expect(response).to render_template(:edit) - end - end - - describe 'DELETE #destroy' do - it 'deletes when not in use' do - allow_any_instance_of(Modelo).to receive(:em_uso?).and_return(false) - modelo_to_delete = create(:modelo) - expect { - delete :destroy, params: { id: modelo_to_delete.id } - }.to change(Modelo, :count).by(-1) - end - - it 'blocks deletion when in use' do - allow_any_instance_of(Modelo).to receive(:em_uso?).and_return(true) - delete :destroy, params: { id: modelo.id } - expect(flash[:alert]).to be_present - end - end - - describe 'POST #clone' do - it 'clones modelo successfully' do - expect { - post :clone, params: { id: modelo.id } - }.to change(Modelo, :count).by(1) - expect(response).to redirect_to(edit_modelo_path(Modelo.last)) - end - end -end diff --git a/spec/controllers/modelos_controller_spec.rb b/spec/controllers/modelos_controller_spec.rb new file mode 100644 index 0000000000..0da3ad4107 --- /dev/null +++ b/spec/controllers/modelos_controller_spec.rb @@ -0,0 +1,231 @@ +require 'rails_helper' + +RSpec.describe ModelosController, type: :controller do + let(:valid_question_type) { "texto_curto" } + + let(:valid_attributes) { + { + titulo: "Modelo Válido", + ativo: true, + perguntas_attributes: [ + { + enunciado: "Qual o seu nome?", + tipo: valid_question_type, + opcoes: [] + } + ] + } + } + + let(:invalid_attributes) { + { titulo: "", ativo: true } + } + + def create_modelo(attributes = {}) + base_params = { titulo: "Modelo Persistido", ativo: true }.merge(attributes.except(:perguntas_attributes)) + modelo = Modelo.new(base_params) + + if modelo.perguntas.empty? + modelo.perguntas.build( + enunciado: "Pergunta Obrigatória", + tipo: valid_question_type, + opcoes: [] + ) + end + + modelo.save! + modelo + end + + before do + user_admin = double("User", eh_admin?: true) + session_obj = double("Session", user: user_admin) + allow(Current).to receive(:session).and_return(session_obj) + end + + describe "Verificação de Permissão" do + context "quando o usuário não é admin" do + before do + user_common = double("User", eh_admin?: false) + session_obj = double("Session", user: user_common) + allow(Current).to receive(:session).and_return(session_obj) + end + + it "redireciona para root_path" do + get :index + expect(response).to redirect_to(root_path) + expect(flash[:alert]).to eq("Acesso restrito a administradores.") + end + end + end + + describe "GET #index" do + it "retorna resposta de sucesso (200 OK)" do + create_modelo + get :index + expect(response).to be_successful + end + end + + describe "GET #show" do + it "retorna resposta de sucesso (200 OK)" do + modelo = create_modelo + get :show, params: { id: modelo.to_param } + expect(response).to be_successful + end + end + + describe "GET #new" do + it "retorna resposta de sucesso (200 OK)" do + get :new + expect(response).to be_successful + end + end + + describe "GET #edit" do + it "retorna resposta de sucesso (200 OK)" do + modelo = create_modelo + get :edit, params: { id: modelo.to_param } + expect(response).to be_successful + end + end + + describe "POST #create" do + context "com parâmetros válidos" do + it "cria um novo Modelo" do + expect { + post :create, params: { modelo: valid_attributes } + }.to change(Modelo, :count).by(1) + end + + it "redireciona para o novo modelo" do + post :create, params: { modelo: valid_attributes } + expect(response).to redirect_to(Modelo.order(created_at: :desc).first) + expect(flash[:notice]).to eq("Modelo criado com sucesso.") + end + end + + context "com parâmetros inválidos" do + it "não cria um novo Modelo" do + expect { + post :create, params: { modelo: invalid_attributes } + }.to change(Modelo, :count).by(0) + end + + it "retorna status 422 (Unprocessable Entity)" do + post :create, params: { modelo: invalid_attributes } + expect(response).to have_http_status(:unprocessable_entity) + end + end + end + + describe "PATCH #update" do + let!(:modelo) { create_modelo } + + context "com parâmetros válidos" do + let(:new_attributes) { + { titulo: "Título Atualizado" } + } + + it "atualiza o Modelo solicitado" do + patch :update, params: { id: modelo.to_param, modelo: new_attributes } + modelo.reload + expect(modelo.titulo).to eq("Título Atualizado") + end + + it "redireciona para o modelo" do + patch :update, params: { id: modelo.to_param, modelo: new_attributes } + expect(response).to redirect_to(modelo) + expect(flash[:notice]).to eq("Modelo atualizado com sucesso.") + end + end + + context "com parâmetros inválidos" do + it "não atualiza o título do modelo" do + old_title = modelo.titulo + patch :update, params: { id: modelo.to_param, modelo: invalid_attributes } + modelo.reload + expect(modelo.titulo).to eq(old_title) + end + + it "retorna status 422 (Unprocessable Entity)" do + patch :update, params: { id: modelo.to_param, modelo: invalid_attributes } + expect(response).to have_http_status(:unprocessable_entity) + end + end + end + + describe "DELETE #destroy" do + let!(:modelo) { create_modelo } + + context "quando o modelo NÃO está em uso" do + before do + allow_any_instance_of(Modelo).to receive(:em_uso?).and_return(false) + end + + it "destrói o modelo solicitado" do + expect { + delete :destroy, params: { id: modelo.to_param } + }.to change(Modelo, :count).by(-1) + end + + it "redireciona para a lista de modelos" do + delete :destroy, params: { id: modelo.to_param } + expect(response).to redirect_to(modelos_url) + expect(flash[:notice]).to eq("Modelo excluído com sucesso.") + end + end + + context "quando o modelo ESTÁ em uso" do + before do + allow_any_instance_of(Modelo).to receive(:em_uso?).and_return(true) + end + + it "NÃO destrói o modelo" do + expect { + delete :destroy, params: { id: modelo.to_param } + }.to change(Modelo, :count).by(0) + end + + it "redireciona para a lista com um alerta" do + delete :destroy, params: { id: modelo.to_param } + expect(response).to redirect_to(modelos_url) + expect(flash[:alert]).to eq("Não é possível excluir um modelo que está em uso.") + end + end + end + + describe "POST #clone" do + let!(:modelo) { create_modelo } + + context "quando a clonagem é bem sucedida" do + it "redireciona para edição do clone" do + novo_modelo = Modelo.new(id: 999, titulo: "Clone") + allow(novo_modelo).to receive(:persisted?).and_return(true) + expect_any_instance_of(Modelo).to receive(:clonar_com_perguntas) + .with("#{modelo.titulo} (Cópia)") + .and_return(novo_modelo) + + post :clone, params: { id: modelo.to_param } + + expect(response).to redirect_to(edit_modelo_path(novo_modelo)) + expect(flash[:notice]).to include("Modelo clonado com sucesso") + end + end + + context "quando a clonagem falha" do + it "redireciona para o modelo original com erro" do + novo_modelo_falho = Modelo.new + allow(novo_modelo_falho).to receive(:persisted?).and_return(false) + + expect_any_instance_of(Modelo).to receive(:clonar_com_perguntas) + .and_return(novo_modelo_falho) + + post :clone, params: { id: modelo.to_param } + + expect(response).to redirect_to(modelo) + expect(flash[:alert]).to eq("Erro ao clonar modelo.") + end + end + end +end \ No newline at end of file From 70a246cf81b1bf5173e5b7e0a3504c32afa39189 Mon Sep 17 00:00:00 2001 From: Marcos Date: Mon, 15 Dec 2025 22:15:09 -0300 Subject: [PATCH 6/6] Corrige problema linter --- spec/controllers/avaliacao_controller_spec.rb | 33 +++++++++---------- spec/controllers/modelos_controller_spec.rb | 8 ++--- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/spec/controllers/avaliacao_controller_spec.rb b/spec/controllers/avaliacao_controller_spec.rb index f5f2485de5..17406c311d 100644 --- a/spec/controllers/avaliacao_controller_spec.rb +++ b/spec/controllers/avaliacao_controller_spec.rb @@ -1,13 +1,12 @@ require 'rails_helper' RSpec.describe AvaliacoesController, type: :controller do - let(:valid_question_type) { "texto_curto" } def create_turma Turma.create!( - codigo: "ENG-#{rand(100..999)}", - nome: "Engenharia de Software", + codigo: "ENG-#{rand(100..999)}", + nome: "Engenharia de Software", semestre: "2025/1" ) end @@ -37,16 +36,16 @@ def create_avaliacao(turma, modelo) def stub_current_user(admin: false, turmas: []) user = double("User", eh_admin?: admin, id: 1) - + allow(controller).to receive(:current_user).and_return(user) - + session_double = double("Session", user: user) allow(Current).to receive(:session).and_return(session_double) allow(Current).to receive(:user).and_return(user) unless admin ids = turmas.map(&:id) - relation = Turma.where(id: ids) + relation = Turma.where(id: ids) allow(user).to receive(:turmas).and_return(relation) end @@ -56,7 +55,7 @@ def stub_current_user(admin: false, turmas: []) describe "GET #index" do context "quando o usuário NÃO está logado" do - before do + before do allow(controller).to receive(:current_user).and_return(nil) allow(Current).to receive(:session).and_return(nil) end @@ -78,9 +77,9 @@ def stub_current_user(admin: false, turmas: []) context "quando o usuário é ALUNO" do let(:turma) { create_turma } - + before do - stub_current_user(admin: false, turmas: [turma]) + stub_current_user(admin: false, turmas: [ turma ]) end it "retorna sucesso (200 OK)" do @@ -105,7 +104,7 @@ def stub_current_user(admin: false, turmas: []) context "Cenários de Falha" do it "redireciona com alerta se a Turma não for encontrada" do - post :create, params: { turma_id: 0 } + post :create, params: { turma_id: 0 } expect(response).to redirect_to(gestao_envios_avaliacoes_path) expect(flash[:alert]).to eq("Turma não encontrada.") end @@ -113,7 +112,7 @@ def stub_current_user(admin: false, turmas: []) it "redireciona com alerta se o Template Padrão não existir" do Modelo.where(titulo: "Template Padrão").destroy_all create_modelo_generico - + post :create, params: { turma_id: turma.id } expect(response).to redirect_to(gestao_envios_avaliacoes_path) expect(flash[:alert]).to include("Template Padrão não encontrado") @@ -147,7 +146,7 @@ def stub_current_user(admin: false, turmas: []) it "redireciona com alerta se o save falhar" do allow_any_instance_of(Avaliacao).to receive(:save).and_return(false) - allow_any_instance_of(Avaliacao).to receive_message_chain(:errors, :full_messages).and_return(["Erro no Banco"]) + allow_any_instance_of(Avaliacao).to receive_message_chain(:errors, :full_messages).and_return([ "Erro no Banco" ]) post :create, params: { turma_id: turma.id } expect(response).to redirect_to(gestao_envios_avaliacoes_path) @@ -158,7 +157,7 @@ def stub_current_user(admin: false, turmas: []) describe "GET #resultados" do before { stub_current_user(admin: true) } - + let!(:turma) { create_turma } let!(:modelo) { create_modelo_generico } let!(:avaliacao) { create_avaliacao(turma, modelo) } @@ -178,7 +177,7 @@ def stub_current_user(admin: false, turmas: []) expect(flash[:alert]).to eq("Erro ao carregar submissões.") expect(response).to be_successful end - + it "executa a lógica de estatísticas sem erro (Happy Path)" do get :resultados, params: { id: avaliacao.id } expect(response).to be_successful @@ -189,15 +188,15 @@ def stub_current_user(admin: false, turmas: []) it "envia o arquivo gerado pelo service" do csv_dummy_content = "Questao,Resposta\n1,Teste" service_double = instance_double("CsvFormatterService") - + expect(CsvFormatterService).to receive(:new).with(avaliacao).and_return(service_double) expect(service_double).to receive(:generate).and_return(csv_dummy_content) get :resultados, params: { id: avaliacao.id }, format: :csv - + expect(response.header['Content-Type']).to include('text/csv') expect(response.body).to eq(csv_dummy_content) end end end -end \ No newline at end of file +end diff --git a/spec/controllers/modelos_controller_spec.rb b/spec/controllers/modelos_controller_spec.rb index 0da3ad4107..3050eae6d3 100644 --- a/spec/controllers/modelos_controller_spec.rb +++ b/spec/controllers/modelos_controller_spec.rb @@ -11,7 +11,7 @@ { enunciado: "Qual o seu nome?", tipo: valid_question_type, - opcoes: [] + opcoes: [] } ] } @@ -28,7 +28,7 @@ def create_modelo(attributes = {}) if modelo.perguntas.empty? modelo.perguntas.build( enunciado: "Pergunta Obrigatória", - tipo: valid_question_type, + tipo: valid_question_type, opcoes: [] ) end @@ -61,7 +61,7 @@ def create_modelo(attributes = {}) describe "GET #index" do it "retorna resposta de sucesso (200 OK)" do - create_modelo + create_modelo get :index expect(response).to be_successful end @@ -228,4 +228,4 @@ def create_modelo(attributes = {}) end end end -end \ No newline at end of file +end