Refactor devices controller and extract registration to model
- Remove Users namespace from DevicesController (now just DevicesController) - Create ApplicationPushDevice model extending ActionPushNative::Device - Move device registration logic (find_or_initialize + update) to model - Update User::Devices concern to use ApplicationPushDevice - Fix push notification tests (endpoint validation, job count expectations) - Update push_config_test to use ActionPushNative.config Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+21
-21
@@ -1,6 +1,6 @@
|
||||
require "test_helper"
|
||||
|
||||
class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
class DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@user = users(:david)
|
||||
sign_in_as @user
|
||||
@@ -11,7 +11,7 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
test "index shows user devices" do
|
||||
@user.devices.create!(token: "test_token_123", platform: "apple", name: "iPhone 15 Pro")
|
||||
|
||||
get users_devices_path
|
||||
get devices_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "strong", "iPhone 15 Pro"
|
||||
@@ -21,7 +21,7 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
test "index shows empty state when no devices" do
|
||||
@user.devices.delete_all
|
||||
|
||||
get users_devices_path
|
||||
get devices_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "p", /No devices registered/
|
||||
@@ -30,7 +30,7 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
test "index requires authentication" do
|
||||
sign_out
|
||||
|
||||
get users_devices_path
|
||||
get devices_path
|
||||
|
||||
assert_response :redirect
|
||||
end
|
||||
@@ -41,7 +41,7 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
token = SecureRandom.hex(32)
|
||||
|
||||
assert_difference "ActionPushNative::Device.count", 1 do
|
||||
post users_devices_path, params: {
|
||||
post devices_path, params: {
|
||||
token: token,
|
||||
platform: "apple",
|
||||
name: "iPhone 15 Pro"
|
||||
@@ -58,7 +58,7 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
end
|
||||
|
||||
test "creates android device" do
|
||||
post users_devices_path, params: {
|
||||
post devices_path, params: {
|
||||
token: SecureRandom.hex(32),
|
||||
platform: "google",
|
||||
name: "Pixel 8"
|
||||
@@ -83,7 +83,7 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
# Current user registers the same token with their own device
|
||||
assert_difference "ActionPushNative::Device.count", 1 do
|
||||
post users_devices_path, params: {
|
||||
post devices_path, params: {
|
||||
token: shared_token,
|
||||
platform: "apple",
|
||||
name: "David's iPhone"
|
||||
@@ -102,7 +102,7 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
end
|
||||
|
||||
test "rejects invalid platform" do
|
||||
post users_devices_path, params: {
|
||||
post devices_path, params: {
|
||||
token: SecureRandom.hex(32),
|
||||
platform: "windows",
|
||||
name: "Surface"
|
||||
@@ -112,7 +112,7 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
end
|
||||
|
||||
test "rejects missing token" do
|
||||
post users_devices_path, params: {
|
||||
post devices_path, params: {
|
||||
platform: "apple",
|
||||
name: "iPhone"
|
||||
}, as: :json
|
||||
@@ -123,7 +123,7 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
test "create requires authentication" do
|
||||
sign_out
|
||||
|
||||
post users_devices_path, params: {
|
||||
post devices_path, params: {
|
||||
token: SecureRandom.hex(32),
|
||||
platform: "apple"
|
||||
}, as: :json
|
||||
@@ -141,19 +141,19 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
)
|
||||
|
||||
assert_difference "ActionPushNative::Device.count", -1 do
|
||||
delete users_device_path(device)
|
||||
delete device_path(device)
|
||||
end
|
||||
|
||||
assert_redirected_to users_devices_path
|
||||
assert_redirected_to devices_path
|
||||
assert_not ActionPushNative::Device.exists?(device.id)
|
||||
end
|
||||
|
||||
test "does nothing when device not found by id" do
|
||||
assert_no_difference "ActionPushNative::Device.count" do
|
||||
delete users_device_path(id: "nonexistent")
|
||||
delete device_path(id: "nonexistent")
|
||||
end
|
||||
|
||||
assert_redirected_to users_devices_path
|
||||
assert_redirected_to devices_path
|
||||
end
|
||||
|
||||
test "cannot destroy another user's device by id" do
|
||||
@@ -165,10 +165,10 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
)
|
||||
|
||||
assert_no_difference "ActionPushNative::Device.count" do
|
||||
delete users_device_path(device)
|
||||
delete device_path(device)
|
||||
end
|
||||
|
||||
assert_redirected_to users_devices_path
|
||||
assert_redirected_to devices_path
|
||||
assert ActionPushNative::Device.exists?(device.id)
|
||||
end
|
||||
|
||||
@@ -181,7 +181,7 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
sign_out
|
||||
|
||||
delete users_device_path(device)
|
||||
delete device_path(device)
|
||||
|
||||
assert_response :redirect
|
||||
assert ActionPushNative::Device.exists?(device.id)
|
||||
@@ -197,7 +197,7 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
)
|
||||
|
||||
assert_difference "ActionPushNative::Device.count", -1 do
|
||||
delete unregister_users_devices_path, params: { token: "token_to_unregister" }, as: :json
|
||||
delete unregister_devices_path, params: { token: "token_to_unregister" }, as: :json
|
||||
end
|
||||
|
||||
assert_response :no_content
|
||||
@@ -206,7 +206,7 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
test "does nothing when device not found by token" do
|
||||
assert_no_difference "ActionPushNative::Device.count" do
|
||||
delete unregister_users_devices_path, params: { token: "nonexistent_token" }, as: :json
|
||||
delete unregister_devices_path, params: { token: "nonexistent_token" }, as: :json
|
||||
end
|
||||
|
||||
assert_response :no_content
|
||||
@@ -221,7 +221,7 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
)
|
||||
|
||||
assert_no_difference "ActionPushNative::Device.count" do
|
||||
delete unregister_users_devices_path, params: { token: "other_users_token" }, as: :json
|
||||
delete unregister_devices_path, params: { token: "other_users_token" }, as: :json
|
||||
end
|
||||
|
||||
assert_response :no_content
|
||||
@@ -237,7 +237,7 @@ class Users::DevicesControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
sign_out
|
||||
|
||||
delete unregister_users_devices_path, params: { token: "my_token" }, as: :json
|
||||
delete unregister_devices_path, params: { token: "my_token" }, as: :json
|
||||
|
||||
assert_response :redirect
|
||||
assert ActionPushNative::Device.exists?(device.id)
|
||||
@@ -59,14 +59,14 @@ class NotificationPusherNativeTest < ActiveSupport::TestCase
|
||||
# === Has Any Push Destination ===
|
||||
|
||||
test "push_destination returns true when user has native devices" do
|
||||
@user.devices.create!(uuid: SecureRandom.uuid, token: "test123", platform: "apple", name: "Test iPhone")
|
||||
@user.devices.create!(token: "test123", platform: "apple", name: "Test iPhone")
|
||||
|
||||
assert @pusher.send(:push_destination?)
|
||||
end
|
||||
|
||||
test "push_destination returns true when user has web subscriptions" do
|
||||
@user.push_subscriptions.create!(
|
||||
endpoint: "https://example.com/push",
|
||||
endpoint: "https://fcm.googleapis.com/fcm/send/test",
|
||||
p256dh_key: "test_p256dh",
|
||||
auth_key: "test_auth"
|
||||
)
|
||||
@@ -85,7 +85,7 @@ class NotificationPusherNativeTest < ActiveSupport::TestCase
|
||||
|
||||
test "push delivers to native devices when user has devices" do
|
||||
stub_push_services
|
||||
@user.devices.create!(uuid: SecureRandom.uuid, token: "test123", platform: "apple", name: "Test iPhone")
|
||||
@user.devices.create!(token: "test123", platform: "apple", name: "Test iPhone")
|
||||
|
||||
assert_native_push_delivery(count: 1) do
|
||||
@pusher.push
|
||||
@@ -102,7 +102,7 @@ class NotificationPusherNativeTest < ActiveSupport::TestCase
|
||||
|
||||
test "push does not deliver when creator is system user" do
|
||||
stub_push_services
|
||||
@user.devices.create!(uuid: SecureRandom.uuid, token: "test123", platform: "apple", name: "Test iPhone")
|
||||
@user.devices.create!(token: "test123", platform: "apple", name: "Test iPhone")
|
||||
@notification.update!(creator: users(:system))
|
||||
|
||||
result = @pusher.push
|
||||
@@ -112,10 +112,11 @@ class NotificationPusherNativeTest < ActiveSupport::TestCase
|
||||
|
||||
test "push delivers to multiple devices" do
|
||||
stub_push_services
|
||||
@user.devices.create!(uuid: SecureRandom.uuid, token: "token1", platform: "apple", name: "iPhone")
|
||||
@user.devices.create!(uuid: SecureRandom.uuid, token: "token2", platform: "google", name: "Pixel")
|
||||
@user.devices.delete_all
|
||||
@user.devices.create!(token: "token1", platform: "apple", name: "iPhone")
|
||||
@user.devices.create!(token: "token2", platform: "google", name: "Pixel")
|
||||
|
||||
assert_native_push_delivery(count: 1) do
|
||||
assert_native_push_delivery(count: 2) do
|
||||
@pusher.push
|
||||
end
|
||||
end
|
||||
@@ -131,7 +132,7 @@ class NotificationPusherNativeTest < ActiveSupport::TestCase
|
||||
)
|
||||
|
||||
# Set up native device
|
||||
@user.devices.create!(uuid: SecureRandom.uuid, token: "native_token", platform: "apple", name: "iPhone")
|
||||
@user.devices.create!(token: "native_token", platform: "apple", name: "iPhone")
|
||||
|
||||
# Mock web push pool to verify it receives the payload
|
||||
web_push_pool = mock("web_push_pool")
|
||||
@@ -149,7 +150,7 @@ class NotificationPusherNativeTest < ActiveSupport::TestCase
|
||||
# === Native Notification Building ===
|
||||
|
||||
test "native notification includes required fields" do
|
||||
@user.devices.create!(uuid: SecureRandom.uuid, token: "test123", platform: "apple", name: "Test iPhone")
|
||||
@user.devices.create!(token: "test123", platform: "apple", name: "Test iPhone")
|
||||
payload = @pusher.send(:build_payload)
|
||||
native = @pusher.send(:native_notification, payload)
|
||||
|
||||
@@ -159,7 +160,7 @@ class NotificationPusherNativeTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
test "native notification sets thread_id from card" do
|
||||
@user.devices.create!(uuid: SecureRandom.uuid, token: "test123", platform: "apple", name: "Test iPhone")
|
||||
@user.devices.create!(token: "test123", platform: "apple", name: "Test iPhone")
|
||||
payload = @pusher.send(:build_payload)
|
||||
native = @pusher.send(:native_notification, payload)
|
||||
|
||||
@@ -168,7 +169,7 @@ class NotificationPusherNativeTest < ActiveSupport::TestCase
|
||||
|
||||
test "native notification sets high_priority for assignments" do
|
||||
notification = notifications(:logo_assignment_kevin)
|
||||
notification.user.devices.create!(uuid: SecureRandom.uuid, token: "test123", platform: "apple", name: "Test iPhone")
|
||||
notification.user.devices.create!(token: "test123", platform: "apple", name: "Test iPhone")
|
||||
pusher = NotificationPusher.new(notification)
|
||||
|
||||
payload = pusher.send(:build_payload)
|
||||
@@ -178,7 +179,7 @@ class NotificationPusherNativeTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
test "native notification sets normal priority for non-assignments" do
|
||||
@user.devices.create!(uuid: SecureRandom.uuid, token: "test123", platform: "apple", name: "Test iPhone")
|
||||
@user.devices.create!(token: "test123", platform: "apple", name: "Test iPhone")
|
||||
payload = @pusher.send(:build_payload)
|
||||
native = @pusher.send(:native_notification, payload)
|
||||
|
||||
@@ -188,7 +189,7 @@ class NotificationPusherNativeTest < ActiveSupport::TestCase
|
||||
# === Apple-specific Payload ===
|
||||
|
||||
test "native notification includes apple-specific fields" do
|
||||
@user.devices.create!(uuid: SecureRandom.uuid, token: "test123", platform: "apple", name: "Test iPhone")
|
||||
@user.devices.create!(token: "test123", platform: "apple", name: "Test iPhone")
|
||||
payload = @pusher.send(:build_payload)
|
||||
native = @pusher.send(:native_notification, payload)
|
||||
|
||||
@@ -200,7 +201,7 @@ class NotificationPusherNativeTest < ActiveSupport::TestCase
|
||||
# === Google-specific Payload ===
|
||||
|
||||
test "native notification sets android notification to nil for data-only" do
|
||||
@user.devices.create!(uuid: SecureRandom.uuid, token: "test123", platform: "apple", name: "Test iPhone")
|
||||
@user.devices.create!(token: "test123", platform: "apple", name: "Test iPhone")
|
||||
payload = @pusher.send(:build_payload)
|
||||
native = @pusher.send(:native_notification, payload)
|
||||
|
||||
@@ -210,11 +211,11 @@ class NotificationPusherNativeTest < ActiveSupport::TestCase
|
||||
# === Data Payload ===
|
||||
|
||||
test "native notification includes data payload" do
|
||||
@user.devices.create!(uuid: SecureRandom.uuid, token: "test123", platform: "apple", name: "Test iPhone")
|
||||
@user.devices.create!(token: "test123", platform: "apple", name: "Test iPhone")
|
||||
payload = @pusher.send(:build_payload)
|
||||
native = @pusher.send(:native_notification, payload)
|
||||
|
||||
assert_not_nil native.data[:path]
|
||||
assert_not_nil native.data[:url]
|
||||
assert_equal @notification.account.external_account_id, native.data[:account_id]
|
||||
assert_equal @notification.creator.name, native.data[:creator_name]
|
||||
end
|
||||
|
||||
@@ -4,11 +4,11 @@ class PushConfigTest < ActiveSupport::TestCase
|
||||
test "loads push config from the saas engine" do
|
||||
skip unless Fizzy.saas?
|
||||
|
||||
config = Rails.application.config_for(:push)
|
||||
config = ActionPushNative.config
|
||||
|
||||
apple_team_id = config.dig("apple", "team_id")
|
||||
apple_topic = config.dig("apple", "topic")
|
||||
google_project_id = config.dig("google", "project_id")
|
||||
apple_team_id = config.dig(:apple, :team_id)
|
||||
apple_topic = config.dig(:apple, :topic)
|
||||
google_project_id = config.dig(:google, :project_id)
|
||||
|
||||
skip "Update test once APNS team_id is configured" if apple_team_id == "YOUR_TEAM_ID"
|
||||
skip "Update test once APNS topic is configured" if apple_topic == "com.yourcompany.fizzy"
|
||||
|
||||
Reference in New Issue
Block a user