Commit iniziale di eminuxCRM: CRM Rails con pipeline, campagne email e Docker.
Include autenticazione, progetti isolati, mail marketing HTML con SMTP, test A/B e editor WYSIWYG. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
require "test_helper"
|
||||
|
||||
class AuthenticationTest < ActionDispatch::IntegrationTest
|
||||
test "requires login" do
|
||||
get root_path
|
||||
assert_redirected_to login_path
|
||||
end
|
||||
|
||||
test "login and logout" do
|
||||
login_as users(:admin)
|
||||
assert_response :redirect
|
||||
follow_redirect!
|
||||
assert_response :success
|
||||
|
||||
delete logout_path
|
||||
assert_redirected_to login_path
|
||||
end
|
||||
|
||||
test "non admin cannot create users" do
|
||||
login_as users(:marco)
|
||||
follow_redirect! if response.redirect?
|
||||
post users_path, params: {
|
||||
user: { email: "x@example.com", first_name: "X", last_name: "Y", role: "user", password: "password123", password_confirmation: "password123" }
|
||||
}
|
||||
assert_response :redirect
|
||||
end
|
||||
|
||||
test "project space is isolated by url" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
get project_root_path(project_code: "matchlivetv")
|
||||
assert_response :success
|
||||
assert_match(/MatchLiveTV/, response.body)
|
||||
|
||||
get project_root_path(project_code: "riskmeter")
|
||||
assert_response :success
|
||||
assert_match(/RiskMeter/, response.body)
|
||||
end
|
||||
|
||||
test "user cannot open disabled project" do
|
||||
login_as users(:marco)
|
||||
follow_redirect! if response.redirect?
|
||||
get project_root_path(project_code: "riskmeter")
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
|
||||
test "disabled user cannot login" do
|
||||
admin = users(:admin)
|
||||
User.create!(
|
||||
email: "keeper@example.com",
|
||||
first_name: "Keep",
|
||||
last_name: "Admin",
|
||||
role: "admin",
|
||||
password: "password123",
|
||||
password_confirmation: "password123"
|
||||
)
|
||||
assert admin.update(active: false)
|
||||
|
||||
post login_path, params: { email: admin.email, password: "password123" }
|
||||
assert_response :unprocessable_entity
|
||||
assert_match(/Email o password non validi/i, response.body)
|
||||
end
|
||||
|
||||
test "admin can open platform settings" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
get admin_path
|
||||
assert_response :success
|
||||
assert_match(/Impostazioni piattaforma/, response.body)
|
||||
assert_match(/Utenti/, response.body)
|
||||
end
|
||||
|
||||
test "non admin cannot open platform settings" do
|
||||
login_as users(:marco)
|
||||
follow_redirect! if response.redirect?
|
||||
get admin_path
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
|
||||
test "admin home has new project and settings actions" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
get root_path
|
||||
assert_response :success
|
||||
assert_match(/Nuovo progetto/, response.body)
|
||||
assert_match(/Impostazioni/, response.body)
|
||||
assert_no_match(/Amministrazione progetti/, response.body)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
require "test_helper"
|
||||
|
||||
class MailIdentitiesControllerTest < ActionDispatch::IntegrationTest
|
||||
test "non admin cannot manage smtp accounts" do
|
||||
login_as users(:marco)
|
||||
follow_redirect! if response.redirect?
|
||||
|
||||
get mail_identities_path
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
|
||||
test "admin can create an smtp account" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
|
||||
assert_difference "MailIdentity.count", 1 do
|
||||
post mail_identities_path, params: {
|
||||
mail_identity: {
|
||||
name: "MatchLiveTV",
|
||||
from_name: "MatchLiveTV",
|
||||
from_email: "hello@matchlivetv.test",
|
||||
smtp_host: "smtp.test",
|
||||
smtp_port: 587,
|
||||
smtp_username: "hello@matchlivetv.test",
|
||||
smtp_password: "super-secret",
|
||||
smtp_authentication: "plain",
|
||||
encryption: "starttls",
|
||||
verify_ssl: "1",
|
||||
active: "1"
|
||||
}
|
||||
}
|
||||
end
|
||||
assert_redirected_to mail_identities_path
|
||||
identity = MailIdentity.find_by!(from_email: "hello@matchlivetv.test")
|
||||
assert_equal "super-secret", identity.smtp_password
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,44 @@
|
||||
require "test_helper"
|
||||
require "base64"
|
||||
|
||||
class MailImagesControllerTest < ActionDispatch::IntegrationTest
|
||||
PNG = Base64.decode64("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==")
|
||||
|
||||
test "admin can upload an image for the editor" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
|
||||
upload = png_upload
|
||||
assert_difference -> { ActiveStorage::Blob.count }, 1 do
|
||||
post mail_images_path(project_code: "matchlivetv"), params: { file: upload }
|
||||
end
|
||||
assert_response :success
|
||||
json = JSON.parse(response.body)
|
||||
assert json["url"].present?
|
||||
assert_match(/active_storage/, json["url"])
|
||||
end
|
||||
|
||||
test "rejects non image files" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
|
||||
file = Tempfile.new(["note", ".txt"])
|
||||
file.write("not-an-image")
|
||||
file.rewind
|
||||
|
||||
post mail_images_path(project_code: "matchlivetv"), params: {
|
||||
file: Rack::Test::UploadedFile.new(file.path, "text/plain")
|
||||
}
|
||||
assert_response :unprocessable_entity
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def png_upload
|
||||
file = Tempfile.new(["pixel", ".png"])
|
||||
file.binmode
|
||||
file.write(PNG)
|
||||
file.rewind
|
||||
Rack::Test::UploadedFile.new(file.path, "image/png")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,107 @@
|
||||
require "test_helper"
|
||||
|
||||
class MailingsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@project = projects(:matchlivetv)
|
||||
@identity = create_mail_identity
|
||||
opportunities(:deal).update!(send_status: "to_send", pipeline_stage: "to_contact")
|
||||
end
|
||||
|
||||
test "lists mailings in project space" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
|
||||
get mailings_path(project_code: @project.code)
|
||||
assert_response :success
|
||||
assert_match(/Email/, response.body)
|
||||
end
|
||||
|
||||
test "creates a draft and builds recipients" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
|
||||
assert_difference -> { Mailing.count } => 1, -> { MailingRecipient.count } => 1 do
|
||||
post mailings_path(project_code: @project.code), params: {
|
||||
mailing: {
|
||||
name: "Lancio",
|
||||
mail_identity_id: @identity.id,
|
||||
audience: "to_send",
|
||||
subject: "Ciao {{societa}}",
|
||||
body_html: "<p>Ciao {{contatto_nome}}</p>",
|
||||
interval_seconds: 0
|
||||
}
|
||||
}
|
||||
end
|
||||
mailing = Mailing.last
|
||||
assert_redirected_to mailing_path(mailing, project_code: @project.code)
|
||||
assert_equal "pending", mailing.mailing_recipients.first.status
|
||||
end
|
||||
|
||||
test "check recipients then queue send" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
mailing = create_mailing(identity: @identity, audience: "to_send")
|
||||
mailing.rebuild_recipients!
|
||||
recipient = mailing.mailing_recipients.first
|
||||
|
||||
patch update_recipients_mailing_path(mailing, project_code: @project.code), params: { pending_ids: [recipient.id] }
|
||||
assert_redirected_to mailing_path(mailing, project_code: @project.code)
|
||||
assert_equal "pending", recipient.reload.status
|
||||
|
||||
perform_enqueued_jobs do
|
||||
post queue_mailing_path(mailing, project_code: @project.code)
|
||||
end
|
||||
|
||||
assert_equal "sent", mailing.reload.status
|
||||
assert_equal "sent", recipient.reload.status
|
||||
assert_equal "sent", opportunities(:deal).reload.send_status
|
||||
assert_equal "contacted", opportunities(:deal).pipeline_stage
|
||||
assert_equal 1, ActionMailer::Base.deliveries.size
|
||||
assert_equal "Ciao ASD Test Calcio", ActionMailer::Base.deliveries.last.subject
|
||||
end
|
||||
|
||||
test "test send goes to current user" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
mailing = create_mailing(identity: @identity, audience: "all")
|
||||
mailing.rebuild_recipients!
|
||||
|
||||
assert_emails 1 do
|
||||
post test_send_mailing_path(mailing, project_code: @project.code)
|
||||
end
|
||||
mail = ActionMailer::Base.deliveries.last
|
||||
assert_equal [users(:admin).email], mail.to
|
||||
assert_match(/\[TEST\]/, mail.subject)
|
||||
end
|
||||
|
||||
test "creates an A/B mailing and sends variant B as test" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
|
||||
post mailings_path(project_code: @project.code), params: {
|
||||
mailing: {
|
||||
name: "AB lancio",
|
||||
mail_identity_id: @identity.id,
|
||||
audience: "to_send",
|
||||
ab_test: "1",
|
||||
ab_assignment: "from_record",
|
||||
subject: "Oggetto A {{societa}}",
|
||||
body_html: "<p>Versione A</p>",
|
||||
subject_b: "Oggetto B {{societa}}",
|
||||
body_html_b: "<p>Versione B</p>",
|
||||
interval_seconds: 0
|
||||
}
|
||||
}
|
||||
mailing = Mailing.last
|
||||
assert mailing.ab_test?
|
||||
assert_equal "A", mailing.mailing_recipients.first.ab_variant
|
||||
|
||||
assert_emails 1 do
|
||||
post test_send_mailing_path(mailing, project_code: @project.code, variant: "B")
|
||||
end
|
||||
mail = ActionMailer::Base.deliveries.last
|
||||
assert_match(/\[TEST B\] Oggetto B ASD Test Calcio/, mail.subject)
|
||||
body = (mail.html_part || mail).body.to_s
|
||||
assert_match(/Versione B/, body)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,117 @@
|
||||
require "test_helper"
|
||||
|
||||
class UsersControllerTest < ActionDispatch::IntegrationTest
|
||||
test "non admin cannot list or create users" do
|
||||
login_as users(:marco)
|
||||
follow_redirect! if response.redirect?
|
||||
|
||||
get users_path
|
||||
assert_redirected_to root_path
|
||||
|
||||
post users_path, params: {
|
||||
user: { email: "x@example.com", first_name: "X", last_name: "Y", role: "user", password: "password123", password_confirmation: "password123" }
|
||||
}
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
|
||||
test "admin can create a user" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
|
||||
assert_difference "User.count", 1 do
|
||||
post users_path, params: {
|
||||
user: {
|
||||
email: "nuovo@example.com",
|
||||
first_name: "Nuovo",
|
||||
last_name: "Utente",
|
||||
role: "user",
|
||||
password: "password123",
|
||||
password_confirmation: "password123"
|
||||
},
|
||||
project_ids: [ projects(:matchlivetv).id ]
|
||||
}
|
||||
end
|
||||
assert_redirected_to users_path
|
||||
user = User.find_by!(email: "nuovo@example.com")
|
||||
assert user.project_enabled?(projects(:matchlivetv))
|
||||
assert_not user.project_enabled?(projects(:riskmeter))
|
||||
end
|
||||
|
||||
test "admin can disable another admin when a second admin exists" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
|
||||
other = User.create!(
|
||||
email: "emiliano@example.com",
|
||||
first_name: "Emiliano",
|
||||
last_name: "Admin",
|
||||
role: "admin",
|
||||
password: "password123",
|
||||
password_confirmation: "password123"
|
||||
)
|
||||
|
||||
patch user_path(other), params: {
|
||||
user: { first_name: other.first_name, last_name: other.last_name, email: other.email, role: "admin", active: "0" }
|
||||
}
|
||||
assert_redirected_to users_path
|
||||
assert_not other.reload.active?
|
||||
end
|
||||
|
||||
test "admin cannot disable last active admin" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
admin = users(:admin)
|
||||
|
||||
patch user_path(admin), params: {
|
||||
user: { first_name: admin.first_name, last_name: admin.last_name, email: admin.email, role: "admin", active: "0" }
|
||||
}
|
||||
assert_response :unprocessable_entity
|
||||
assert admin.reload.active?
|
||||
assert_match(/almeno un amministratore attivo/i, response.body)
|
||||
end
|
||||
|
||||
test "admin cannot delete self" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
|
||||
assert_no_difference "User.count" do
|
||||
delete user_path(users(:admin))
|
||||
end
|
||||
assert_redirected_to users_path
|
||||
follow_redirect!
|
||||
assert_match(/Non puoi eliminare il tuo account/i, response.body)
|
||||
end
|
||||
|
||||
test "admin can delete another admin when a second remains" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
|
||||
other = User.create!(
|
||||
email: "other-admin@example.com",
|
||||
first_name: "Other",
|
||||
last_name: "Admin",
|
||||
role: "admin",
|
||||
password: "password123",
|
||||
password_confirmation: "password123"
|
||||
)
|
||||
|
||||
assert_difference "User.count", -1 do
|
||||
delete user_path(other)
|
||||
end
|
||||
assert_redirected_to users_path
|
||||
end
|
||||
|
||||
test "admin can delete another user and nullify activities" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
marco = users(:marco)
|
||||
activity = activities(:note)
|
||||
assert_equal marco.id, activity.user_id
|
||||
|
||||
assert_difference "User.count", -1 do
|
||||
delete user_path(marco)
|
||||
end
|
||||
assert_redirected_to users_path
|
||||
assert_nil activity.reload.user_id
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user