+
+
+
Systems
+
- <% if @available_channels.any? %>
+ <% if @zone.editable_by?(current_user) %>
+ <% available_systems = System.where.not(id: @zone.zone_systems.pluck(:system_id)).order(:name) %>
+ <% if available_systems.any? %>
- <%= form_with(model: @zone.channel_zones.new, url: codeplug_zone_channel_zones_path(@codeplug, @zone), method: :post, local: true, class: "row g-3") do |f| %>
+ <%= form_with(model: @zone.zone_systems.new, url: zone_zone_systems_path(@zone), method: :post, local: true, class: "row g-3") do |f| %>
- <%= f.select :channel_id,
- options_from_collection_for_select(@available_channels, :id, :long_name),
- { prompt: "Select a channel to add..." },
+ <%= f.select :system_id,
+ options_from_collection_for_select(available_systems, :id, :name),
+ { prompt: "Select a system to add..." },
{ class: "form-select" } %>
- <%= f.submit "Add Channel", class: "btn btn-primary" %>
+ <%= f.submit "Add System", class: "btn btn-primary" %>
<% end %>
<% end %>
+ <% end %>
- <% if @zone.channel_zones.any? %>
-
<%= pluralize(@zone.channel_zones.count, "channel") %>
-
- <% @zone.channel_zones.order(:position).each do |channel_zone| %>
-
-
-
+ <% if @zone_systems.any? %>
+
<%= pluralize(@zone_systems.count, "system") %>
+
+ <% @zone_systems.each do |zone_system| %>
+
+
+
+ <% if @zone.editable_by?(current_user) %>
-
-
<%= channel_zone.channel.long_name %>
- <% if channel_zone.channel.system %>
-
<%= channel_zone.channel.system.mode.upcase %>
+ <% end %>
+
+ <%= zone_system.system.name %>
+ <%= zone_system.system.mode.upcase %>
+
+
+ <%= zone_system.system.rx_frequency %> MHz
+ <% if zone_system.system.tx_frequency != zone_system.system.rx_frequency %>
+ / <%= zone_system.system.tx_frequency %> MHz
<% end %>
-
-
<%= channel_zone.position %>
- <%= link_to "Edit", edit_codeplug_channel_path(@codeplug, channel_zone.channel), class: "btn btn-sm btn-outline-secondary me-2" %>
- <%= button_to "Remove", codeplug_zone_channel_zone_path(@codeplug, @zone, channel_zone),
- method: :delete,
- class: "btn btn-sm btn-outline-danger",
- form: { class: "d-inline" },
- data: { turbo_confirm: "Remove this channel from the zone?" } %>
-
-
-
- <% end %>
-
- <% else %>
-
No channels in this zone yet. <%= "Add channels using the form above." if @available_channels.any? %>
- <% end %>
-
-
- <% else %>
- <%# Standalone zone view - show systems %>
-
-
-
-
Systems
-
-
- <% if @zone.editable_by?(current_user) %>
- <% available_systems = System.where.not(id: @zone.zone_systems.pluck(:system_id)).order(:name) %>
- <% if available_systems.any? %>
-
-
- <%= form_with(model: @zone.zone_systems.new, url: zone_zone_systems_path(@zone), method: :post, local: true, class: "row g-3") do |f| %>
-
- <%= f.select :system_id,
- options_from_collection_for_select(available_systems, :id, :name),
- { prompt: "Select a system to add..." },
- { class: "form-select" } %>
-
-
- <%= f.submit "Add System", class: "btn btn-primary" %>
-
- <% end %>
-
-
- <% end %>
- <% end %>
-
- <% if @zone_systems.any? %>
-
<%= pluralize(@zone_systems.count, "system") %>
-
- <% @zone_systems.each do |zone_system| %>
-
-
-
- <% if @zone.editable_by?(current_user) %>
-
-
-
-
-
- <% end %>
-
-
<%= zone_system.system.name %>
-
<%= zone_system.system.mode.upcase %>
-
-
- <%= zone_system.system.rx_frequency %> MHz
- <% if zone_system.system.tx_frequency != zone_system.system.rx_frequency %>
- / <%= zone_system.system.tx_frequency %> MHz
- <% end %>
-
- <% if zone_system.system.mode == "dmr" || zone_system.system.mode == "p25" %>
-
-
Talkgroups:
- <% if zone_system.zone_system_talkgroups.any? %>
-
- <% zone_system.zone_system_talkgroups.includes(system_talk_group: :talk_group).each do |zstg| %>
-
- <%= zstg.system_talk_group.talk_group.name %>
- (<%= zstg.system_talk_group.talk_group.talkgroup_number %>)
- TS<%= zstg.system_talk_group.timeslot %>
- <% if @zone.editable_by?(current_user) %>
- <%= button_to zone_zone_system_zone_system_talkgroup_path(@zone, zone_system, zstg),
- method: :delete,
- class: "btn btn-sm p-0 ms-1 text-danger",
- form: { class: "d-inline" },
- data: { turbo_confirm: "Remove this talkgroup?" } do %>
-
-
-
- <% end %>
+
+ <% if zone_system.system.mode == "dmr" || zone_system.system.mode == "p25" %>
+
+
Talkgroups:
+ <% if zone_system.zone_system_talkgroups.any? %>
+
+ <% zone_system.zone_system_talkgroups.includes(system_talk_group: :talk_group).each do |zstg| %>
+
+ <%= zstg.system_talk_group.talk_group.name %>
+ (<%= zstg.system_talk_group.talk_group.talkgroup_number %>)
+ TS<%= zstg.system_talk_group.timeslot %>
+ <% if @zone.editable_by?(current_user) %>
+ <%= button_to zone_zone_system_zone_system_talkgroup_path(@zone, zone_system, zstg),
+ method: :delete,
+ class: "btn btn-sm p-0 ms-1 text-danger",
+ form: { class: "d-inline" },
+ data: { turbo_confirm: "Remove this talkgroup?" } do %>
+
+
+
<% end %>
-
- <% end %>
-
- <% else %>
-
None selected
- <% end %>
- <% if @zone.editable_by?(current_user) %>
- <% available_talkgroups = zone_system.system.system_talk_groups.includes(:talk_group).where.not(id: zone_system.zone_system_talkgroups.pluck(:system_talk_group_id)) %>
- <% if available_talkgroups.any? %>
-
- <%= form_with(model: zone_system.zone_system_talkgroups.new, url: zone_zone_system_zone_system_talkgroups_path(@zone, zone_system), method: :post, local: true, class: "d-flex gap-2 align-items-center") do |f| %>
- <%= f.select :system_talk_group_id,
- available_talkgroups.map { |stg| ["#{stg.talk_group.name} (#{stg.talk_group.talkgroup_number}) - TS#{stg.timeslot}", stg.id] },
- { prompt: "Select talkgroup..." },
- { class: "form-select form-select-sm", style: "width: auto;" } %>
- <%= f.submit "Add Talkgroup", class: "btn btn-sm btn-outline-primary" %>
<% end %>
-
+
<% end %>
+
+ <% else %>
+ None selected
+ <% end %>
+ <% if @zone.editable_by?(current_user) %>
+ <% available_talkgroups = zone_system.system.system_talk_groups.includes(:talk_group).where.not(id: zone_system.zone_system_talkgroups.pluck(:system_talk_group_id)) %>
+ <% if available_talkgroups.any? %>
+
+ <%= form_with(model: zone_system.zone_system_talkgroups.new, url: zone_zone_system_zone_system_talkgroups_path(@zone, zone_system), method: :post, local: true, class: "d-flex gap-2 align-items-center") do |f| %>
+ <%= f.select :system_talk_group_id,
+ available_talkgroups.map { |stg| ["#{stg.talk_group.name} (#{stg.talk_group.talkgroup_number}) - TS#{stg.timeslot}", stg.id] },
+ { prompt: "Select talkgroup..." },
+ { class: "form-select form-select-sm", style: "width: auto;" } %>
+ <%= f.submit "Add Talkgroup", class: "btn btn-sm btn-outline-primary" %>
+ <% end %>
+
<% end %>
-
- <% end %>
-
-
<%= zone_system.position %>
- <% if @zone.editable_by?(current_user) %>
- <%= button_to "Remove", zone_zone_system_path(@zone, zone_system),
- method: :delete,
- class: "btn btn-sm btn-outline-danger",
- form: { class: "d-inline" },
- data: { turbo_confirm: "Remove this system from the zone?" } %>
+ <% end %>
+
<% end %>
+
<%= zone_system.position %>
+ <% if @zone.editable_by?(current_user) %>
+ <%= button_to "Remove", zone_zone_system_path(@zone, zone_system),
+ method: :delete,
+ class: "btn btn-sm btn-outline-danger",
+ form: { class: "d-inline" },
+ data: { turbo_confirm: "Remove this system from the zone?" } %>
+ <% end %>
- <% end %>
-
- <% else %>
-
No systems in this zone yet. <%= "Add systems using the form above." if @zone.editable_by?(current_user) && System.count > 0 %>
- <% end %>
-
+
+ <% end %>
+
+ <% else %>
+
No systems in this zone yet. <%= "Add systems using the form above." if @zone.editable_by?(current_user) && System.count > 0 %>
+ <% end %>
- <% end %>
+
diff --git a/config/routes.rb b/config/routes.rb
index 683c5f0..a63f633 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -52,13 +52,6 @@
patch :update_positions
end
end
- # Nested zones routes (kept for backward compatibility, will be deprecated)
- resources :zones do
- resources :channel_zones, only: [ :create, :destroy ]
- member do
- patch :update_positions
- end
- end
resources :channels
end
diff --git a/db/migrate/20251218005215_remove_codeplug_id_from_zones.rb b/db/migrate/20251218005215_remove_codeplug_id_from_zones.rb
new file mode 100644
index 0000000..a699846
--- /dev/null
+++ b/db/migrate/20251218005215_remove_codeplug_id_from_zones.rb
@@ -0,0 +1,12 @@
+class RemoveCodeplugIdFromZones < ActiveRecord::Migration[8.1]
+ def change
+ # Remove the foreign key constraint first
+ remove_foreign_key :zones, :codeplugs
+
+ # Remove the index
+ remove_index :zones, :codeplug_id
+
+ # Remove the column
+ remove_column :zones, :codeplug_id, :bigint
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index da3c5c0..7bcba1a 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.1].define(version: 2025_12_15_054858) do
+ActiveRecord::Schema[8.1].define(version: 2025_12_18_005215) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
@@ -227,7 +227,6 @@
end
create_table "zones", force: :cascade do |t|
- t.bigint "codeplug_id"
t.datetime "created_at", null: false
t.string "long_name"
t.string "name", null: false
@@ -235,7 +234,6 @@
t.string "short_name"
t.datetime "updated_at", null: false
t.bigint "user_id", null: false
- t.index ["codeplug_id"], name: "index_zones_on_codeplug_id"
t.index ["public"], name: "index_zones_on_public"
t.index ["user_id"], name: "index_zones_on_user_id"
end
@@ -263,6 +261,5 @@
add_foreign_key "zone_system_talk_groups", "zone_systems"
add_foreign_key "zone_systems", "systems"
add_foreign_key "zone_systems", "zones"
- add_foreign_key "zones", "codeplugs"
add_foreign_key "zones", "users"
end
diff --git a/db/seeds.rb b/db/seeds.rb
index cc0d224..24165d1 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -222,6 +222,151 @@
end
puts "Created #{radio_models_data.count} system radio models"
+ # Create networks for DMR talkgroups
+ networks_data = [
+ { name: "Brandmeister", network_type: "Digital-DMR", description: "Worldwide DMR network" },
+ { name: "TGIF", network_type: "Digital-DMR", description: "The Global Internet of Friends" },
+ { name: "P25 Network", network_type: "Digital-P25", description: "Project 25 digital network" }
+ ]
+
+ networks = networks_data.map do |data|
+ network = Network.find_or_create_by!(name: data[:name]) do |n|
+ n.network_type = data[:network_type]
+ n.description = data[:description]
+ end
+ network
+ end
+ puts "Created #{networks.count} networks"
+
+ # Create some sample systems (repeaters)
+ dmr_network = Network.find_by!(name: "Brandmeister")
+
+ # Analog system
+ analog_detail = AnalogModeDetail.find_or_create_by!(id: AnalogModeDetail.maximum(:id).to_i + 1)
+ analog_system = System.find_or_create_by!(name: "W1DEV Analog Repeater") do |s|
+ s.mode = "analog"
+ s.rx_frequency = 146.82
+ s.tx_frequency = 146.22
+ s.supports_tx_tone = true
+ s.tx_tone_value = "127.3"
+ s.supports_rx_tone = true
+ s.rx_tone_value = "127.3"
+ s.mode_detail = analog_detail
+ end
+ puts "Created analog system: #{analog_system.name}"
+
+ # DMR system
+ dmr_detail = DmrModeDetail.find_or_create_by!(color_code: 3) do |d|
+ d.id = DmrModeDetail.maximum(:id).to_i + 1
+ end
+ dmr_system = System.find_or_create_by!(name: "W1DEV DMR Repeater") do |s|
+ s.mode = "dmr"
+ s.rx_frequency = 446.5
+ s.tx_frequency = 441.5
+ s.mode_detail = dmr_detail
+ end
+ # Associate DMR system with network
+ unless dmr_system.networks.include?(dmr_network)
+ dmr_system.networks << dmr_network
+ end
+ puts "Created DMR system: #{dmr_system.name}"
+
+ # Create talkgroups
+ talkgroups_data = [
+ { name: "Local", talkgroup_number: "3100", network: dmr_network },
+ { name: "TAC 310", talkgroup_number: "310", network: dmr_network },
+ { name: "North America", talkgroup_number: "3", network: dmr_network }
+ ]
+
+ talkgroups = talkgroups_data.map do |data|
+ TalkGroup.find_or_create_by!(network: data[:network], talkgroup_number: data[:talkgroup_number]) do |tg|
+ tg.name = data[:name]
+ end
+ end
+ puts "Created #{talkgroups.count} talkgroups"
+
+ # Create SystemTalkGroups (talkgroups available on the DMR system)
+ stg_data = [
+ { talk_group: TalkGroup.find_by!(name: "Local"), timeslot: 1 },
+ { talk_group: TalkGroup.find_by!(name: "TAC 310"), timeslot: 2 },
+ { talk_group: TalkGroup.find_by!(name: "North America"), timeslot: 1 }
+ ]
+
+ stg_data.each do |data|
+ SystemTalkGroup.find_or_create_by!(
+ system: dmr_system,
+ talk_group: data[:talk_group],
+ timeslot: data[:timeslot]
+ )
+ end
+ puts "Created #{stg_data.count} system talkgroups"
+
+ # Create standalone zones (new architecture)
+ # Zone 1: Local analog repeaters
+ analog_zone = Zone.find_or_create_by!(user: user, name: "Local Analog") do |z|
+ z.long_name = "Local Analog Repeaters"
+ z.short_name = "ANALG"
+ z.public = false
+ end
+
+ # Add analog system to zone
+ unless analog_zone.zone_systems.exists?(system: analog_system)
+ ZoneSystem.create!(zone: analog_zone, system: analog_system, position: 1)
+ end
+ puts "Created zone: #{analog_zone.name} with #{analog_zone.zone_systems.count} system(s)"
+
+ # Zone 2: DMR with talkgroups
+ dmr_zone = Zone.find_or_create_by!(user: user, name: "DMR Repeaters") do |z|
+ z.long_name = "DMR Repeater Zone"
+ z.short_name = "DMR"
+ z.public = false
+ end
+
+ # Add DMR system to zone
+ zone_system = dmr_zone.zone_systems.find_or_create_by!(system: dmr_system) do |zs|
+ zs.position = 1
+ end
+
+ # Add talkgroups to zone_system
+ stg_data.each do |data|
+ stg = SystemTalkGroup.find_by!(system: dmr_system, talk_group: data[:talk_group])
+ unless zone_system.zone_system_talkgroups.exists?(system_talk_group: stg)
+ ZoneSystemTalkGroup.create!(zone_system: zone_system, system_talk_group: stg)
+ end
+ end
+ puts "Created zone: #{dmr_zone.name} with #{dmr_zone.zone_systems.count} system(s) and #{zone_system.zone_system_talkgroups.count} talkgroups"
+
+ # Create a public zone for sharing
+ public_zone = Zone.find_or_create_by!(user: user, name: "Sample Public Zone") do |z|
+ z.long_name = "Sample Public Zone"
+ z.short_name = "PUB"
+ z.public = true
+ end
+ puts "Created public zone: #{public_zone.name}"
+
+ # Create a codeplug and add zones to it (new architecture)
+ codeplug = Codeplug.find_or_create_by!(user: user, name: "My First Codeplug") do |c|
+ c.description = "A sample codeplug demonstrating the zone architecture"
+ c.public = false
+ end
+
+ # Add zones to codeplug via CodeplugZone
+ [ analog_zone, dmr_zone ].each_with_index do |zone, index|
+ CodeplugZone.find_or_create_by!(codeplug: codeplug, zone: zone) do |cz|
+ cz.position = index + 1
+ end
+ end
+ puts "Created codeplug: #{codeplug.name} with #{codeplug.codeplug_zones.count} zone(s)"
+
+ # Generate channels from zones
+ if codeplug.channels.empty?
+ generator = ChannelGenerator.new(codeplug)
+ result = generator.generate_channels
+ puts "Generated #{result[:channels_created]} channel(s) from #{result[:zones_processed]} zone(s)"
+ else
+ puts "Codeplug already has #{codeplug.channels.count} channel(s)"
+ end
+
puts "\n=== Seed data complete ==="
puts "Login with: dev@example.com / password123"
puts "Run 'rails db:seed' again to update seed data (idempotent)"
diff --git a/test/controllers/channel_zones_controller_test.rb b/test/controllers/channel_zones_controller_test.rb
deleted file mode 100644
index 3ec3a00..0000000
--- a/test/controllers/channel_zones_controller_test.rb
+++ /dev/null
@@ -1,143 +0,0 @@
-require "test_helper"
-
-class ChannelZonesControllerTest < ActionDispatch::IntegrationTest
- setup do
- @user = create(:user)
- @other_user = create(:user)
- @codeplug = create(:codeplug, user: @user)
- @other_codeplug = create(:codeplug, user: @other_user)
- @zone = create(:zone, codeplug: @codeplug, name: "Zone 1")
- @other_zone = create(:zone, codeplug: @other_codeplug, name: "Other Zone")
- @system = create(:system)
- @channel = create(:channel, codeplug: @codeplug, system: @system, long_name: "Channel 1")
- @other_channel = create(:channel, codeplug: @other_codeplug, system: @system, long_name: "Other Channel")
- end
-
- # Create Action Tests
- test "should add channel to own zone" do
- log_in_as(@user)
-
- assert_difference("ChannelZone.count", 1) do
- post codeplug_zone_channel_zones_path(@codeplug, @zone), params: {
- channel_zone: {
- channel_id: @channel.id
- }
- }
- end
-
- channel_zone = ChannelZone.last
- assert_equal @zone, channel_zone.zone
- assert_equal @channel, channel_zone.channel
- assert_equal 1, channel_zone.position
- assert_redirected_to codeplug_zone_path(@codeplug, @zone)
- assert_equal "Channel was successfully added to zone.", flash[:notice]
- end
-
- test "should set correct position when adding channel to zone with existing channels" do
- log_in_as(@user)
- channel2 = create(:channel, codeplug: @codeplug, system: @system, long_name: "Channel 2")
- channel3 = create(:channel, codeplug: @codeplug, system: @system, long_name: "Channel 3")
- create(:channel_zone, zone: @zone, channel: @channel, position: 1)
- create(:channel_zone, zone: @zone, channel: channel2, position: 2)
-
- assert_difference("ChannelZone.count", 1) do
- post codeplug_zone_channel_zones_path(@codeplug, @zone), params: {
- channel_zone: {
- channel_id: channel3.id
- }
- }
- end
-
- channel_zone = ChannelZone.last
- assert_equal 3, channel_zone.position
- end
-
- test "should not add channel from other user's codeplug to zone" do
- log_in_as(@user)
-
- assert_no_difference("ChannelZone.count") do
- post codeplug_zone_channel_zones_path(@codeplug, @zone), params: {
- channel_zone: {
- channel_id: @other_channel.id
- }
- }
- end
-
- assert_response :unprocessable_entity
- end
-
- test "should not add duplicate channel to zone" do
- log_in_as(@user)
- create(:channel_zone, zone: @zone, channel: @channel, position: 1)
-
- assert_no_difference("ChannelZone.count") do
- post codeplug_zone_channel_zones_path(@codeplug, @zone), params: {
- channel_zone: {
- channel_id: @channel.id
- }
- }
- end
-
- assert_response :unprocessable_entity
- end
-
- test "should not add channel to other user's zone" do
- log_in_as(@user)
-
- assert_no_difference("ChannelZone.count") do
- post codeplug_zone_channel_zones_path(@other_codeplug, @other_zone), params: {
- channel_zone: {
- channel_id: @channel.id
- }
- }
- end
-
- assert_redirected_to codeplugs_path
- end
-
- test "should require login for create" do
- assert_no_difference("ChannelZone.count") do
- post codeplug_zone_channel_zones_path(@codeplug, @zone), params: {
- channel_zone: {
- channel_id: @channel.id
- }
- }
- end
-
- assert_redirected_to login_path
- end
-
- # Destroy Action Tests
- test "should remove channel from own zone" do
- log_in_as(@user)
- channel_zone = create(:channel_zone, zone: @zone, channel: @channel, position: 1)
-
- assert_difference("ChannelZone.count", -1) do
- delete codeplug_zone_channel_zone_path(@codeplug, @zone, channel_zone)
- end
-
- assert_redirected_to codeplug_zone_path(@codeplug, @zone)
- assert_equal "Channel was successfully removed from zone.", flash[:notice]
- end
-
- test "should not remove channel from other user's zone" do
- log_in_as(@user)
- other_channel_zone = create(:channel_zone, zone: @other_zone, channel: @other_channel, position: 1)
-
- assert_no_difference("ChannelZone.count") do
- delete codeplug_zone_channel_zone_path(@other_codeplug, @other_zone, other_channel_zone)
- end
-
- assert_redirected_to codeplugs_path
- end
-
- test "should require login for destroy" do
- channel_zone = create(:channel_zone, zone: @zone, channel: @channel, position: 1)
-
- assert_no_difference("ChannelZone.count") do
- delete codeplug_zone_channel_zone_path(@codeplug, @zone, channel_zone)
- end
-
- assert_redirected_to login_path
- end
-end
diff --git a/test/controllers/zones_controller_test.rb b/test/controllers/zones_controller_test.rb
index a5b0896..921111b 100644
--- a/test/controllers/zones_controller_test.rb
+++ b/test/controllers/zones_controller_test.rb
@@ -4,93 +4,108 @@ class ZonesControllerTest < ActionDispatch::IntegrationTest
setup do
@user = create(:user)
@other_user = create(:user)
- @codeplug = create(:codeplug, user: @user)
- @other_codeplug = create(:codeplug, user: @other_user)
- @zone = create(:zone, codeplug: @codeplug, name: "Zone 1")
- @other_zone = create(:zone, codeplug: @other_codeplug, name: "Other Zone")
+ @zone = create(:zone, user: @user, name: "Zone 1")
+ @other_zone = create(:zone, user: @other_user, name: "Other Zone")
end
# Index Action Tests
- test "should get index for own codeplug" do
+ test "should show user's zones and public zones" do
log_in_as(@user)
- get codeplug_zones_path(@codeplug)
- assert_response :success
- assert_select "h1", "Zones"
- end
+ # Create test data
+ my_public_zone = create(:zone, user: @user, public: true, name: "My Public Zone")
+ my_private_zone = create(:zone, user: @user, public: false, name: "My Private Zone")
+ other_public_zone = create(:zone, user: @other_user, public: true, name: "Other Public Zone")
+ other_private_zone = create(:zone, user: @other_user, public: false, name: "Other Private Zone")
- test "should not get index for other user's codeplug" do
- log_in_as(@user)
- get codeplug_zones_path(@other_codeplug)
+ get zones_path
- assert_redirected_to codeplugs_path
- assert_equal "You don't have permission to access this codeplug.", flash[:alert]
+ assert_response :success
+ # Should see own zones (both public and private)
+ assert_select "body", text: /My Public Zone/
+ assert_select "body", text: /My Private Zone/
+ # Should see other users' public zones
+ assert_select "body", text: /Other Public Zone/
+ # Should NOT see other users' private zones
+ assert_select "body", { text: /Other Private Zone/, count: 0 }
end
test "should require login for index" do
- get codeplug_zones_path(@codeplug)
+ get zones_path
assert_redirected_to login_path
end
# Show Action Tests
- test "should show zone for own codeplug" do
+ test "should display public zone to any user" do
log_in_as(@user)
- get codeplug_zone_path(@codeplug, @zone)
+ public_zone = create(:zone, user: @other_user, public: true, name: "Public Zone")
+
+ get zone_path(public_zone)
assert_response :success
- assert_select "h1", @zone.name
+ assert_select "h1", "Public Zone"
end
- test "should not show zone from other user's codeplug" do
+ test "should display own private zone" do
log_in_as(@user)
- get codeplug_zone_path(@other_codeplug, @other_zone)
+ my_private_zone = create(:zone, user: @user, public: false, name: "My Private Zone")
- assert_redirected_to codeplugs_path
+ get zone_path(my_private_zone)
+
+ assert_response :success
+ assert_select "h1", "My Private Zone"
+ end
+
+ test "should not display other user's private zone" do
+ log_in_as(@user)
+ other_private_zone = create(:zone, user: @other_user, public: false, name: "Private Zone")
+
+ get zone_path(other_private_zone)
+
+ assert_response :forbidden
end
test "should require login for show" do
- get codeplug_zone_path(@codeplug, @zone)
+ public_zone = create(:zone, user: @user, public: true)
+
+ get zone_path(public_zone)
+
assert_redirected_to login_path
end
# New Action Tests
- test "should get new for own codeplug" do
+ test "should get new" do
log_in_as(@user)
- get new_codeplug_zone_path(@codeplug)
+ get new_zone_path
assert_response :success
assert_select "h1", "New Zone"
end
- test "should not get new for other user's codeplug" do
- log_in_as(@user)
- get new_codeplug_zone_path(@other_codeplug)
-
- assert_redirected_to codeplugs_path
- end
-
test "should require login for new" do
- get new_codeplug_zone_path(@codeplug)
+ get new_zone_path
assert_redirected_to login_path
end
# Create Action Tests
- test "should create zone for own codeplug" do
+ test "should create zone for logged in user" do
log_in_as(@user)
assert_difference("Zone.count", 1) do
- post codeplug_zones_path(@codeplug), params: {
+ post zones_path, params: {
zone: {
name: "New Zone",
long_name: "New Long Zone Name",
- short_name: "NZN"
+ short_name: "NZN",
+ public: false
}
}
end
zone = Zone.last
- assert_equal @codeplug, zone.codeplug
- assert_redirected_to codeplug_zone_path(@codeplug, zone)
+ assert_equal @user, zone.user
+ assert_equal "New Zone", zone.name
+ assert_redirected_to zone_path(zone)
assert_equal "Zone was successfully created.", flash[:notice]
end
@@ -98,7 +113,7 @@ class ZonesControllerTest < ActionDispatch::IntegrationTest
log_in_as(@user)
assert_no_difference("Zone.count") do
- post codeplug_zones_path(@codeplug), params: {
+ post zones_path, params: {
zone: {
name: "",
long_name: "Test"
@@ -109,23 +124,9 @@ class ZonesControllerTest < ActionDispatch::IntegrationTest
assert_response :unprocessable_entity
end
- test "should not create zone for other user's codeplug" do
- log_in_as(@user)
-
- assert_no_difference("Zone.count") do
- post codeplug_zones_path(@other_codeplug), params: {
- zone: {
- name: "Hacked Zone"
- }
- }
- end
-
- assert_redirected_to codeplugs_path
- end
-
test "should require login for create" do
assert_no_difference("Zone.count") do
- post codeplug_zones_path(@codeplug), params: {
+ post zones_path, params: {
zone: {
name: "Test"
}
@@ -138,7 +139,7 @@ class ZonesControllerTest < ActionDispatch::IntegrationTest
# Edit Action Tests
test "should get edit for own zone" do
log_in_as(@user)
- get edit_codeplug_zone_path(@codeplug, @zone)
+ get edit_zone_path(@zone)
assert_response :success
assert_select "h1", text: /Edit Zone/
@@ -146,13 +147,13 @@ class ZonesControllerTest < ActionDispatch::IntegrationTest
test "should not get edit for other user's zone" do
log_in_as(@user)
- get edit_codeplug_zone_path(@other_codeplug, @other_zone)
+ get edit_zone_path(@other_zone)
- assert_redirected_to codeplugs_path
+ assert_response :forbidden
end
test "should require login for edit" do
- get edit_codeplug_zone_path(@codeplug, @zone)
+ get edit_zone_path(@zone)
assert_redirected_to login_path
end
@@ -160,7 +161,7 @@ class ZonesControllerTest < ActionDispatch::IntegrationTest
test "should update own zone" do
log_in_as(@user)
- patch codeplug_zone_path(@codeplug, @zone), params: {
+ patch zone_path(@zone), params: {
zone: {
name: "Updated Zone",
long_name: "Updated Long Name"
@@ -170,14 +171,14 @@ class ZonesControllerTest < ActionDispatch::IntegrationTest
@zone.reload
assert_equal "Updated Zone", @zone.name
assert_equal "Updated Long Name", @zone.long_name
- assert_redirected_to codeplug_zone_path(@codeplug, @zone)
+ assert_redirected_to zone_path(@zone)
assert_equal "Zone was successfully updated.", flash[:notice]
end
test "should not update without name" do
log_in_as(@user)
- patch codeplug_zone_path(@codeplug, @zone), params: {
+ patch zone_path(@zone), params: {
zone: {
name: ""
}
@@ -190,7 +191,7 @@ class ZonesControllerTest < ActionDispatch::IntegrationTest
log_in_as(@user)
original_name = @other_zone.name
- patch codeplug_zone_path(@other_codeplug, @other_zone), params: {
+ patch zone_path(@other_zone), params: {
zone: {
name: "Hacked Name"
}
@@ -198,11 +199,11 @@ class ZonesControllerTest < ActionDispatch::IntegrationTest
@other_zone.reload
assert_equal original_name, @other_zone.name
- assert_redirected_to codeplugs_path
+ assert_response :forbidden
end
test "should require login for update" do
- patch codeplug_zone_path(@codeplug, @zone), params: {
+ patch zone_path(@zone), params: {
zone: {
name: "New Name"
}
@@ -216,10 +217,10 @@ class ZonesControllerTest < ActionDispatch::IntegrationTest
log_in_as(@user)
assert_difference("Zone.count", -1) do
- delete codeplug_zone_path(@codeplug, @zone)
+ delete zone_path(@zone)
end
- assert_redirected_to codeplug_zones_path(@codeplug)
+ assert_redirected_to zones_path
assert_equal "Zone was successfully deleted.", flash[:notice]
end
@@ -227,222 +228,67 @@ class ZonesControllerTest < ActionDispatch::IntegrationTest
log_in_as(@user)
assert_no_difference("Zone.count") do
- delete codeplug_zone_path(@other_codeplug, @other_zone)
+ delete zone_path(@other_zone)
end
- assert_redirected_to codeplugs_path
+ assert_response :forbidden
end
test "should require login for destroy" do
assert_no_difference("Zone.count") do
- delete codeplug_zone_path(@codeplug, @zone)
+ delete zone_path(@zone)
end
assert_redirected_to login_path
end
- # Update Positions Action Tests
- test "should update channel positions for own zone" do
+ # Update Positions Action Tests (for zone_systems reordering)
+ test "should update zone_system positions for own zone" do
log_in_as(@user)
- system = create(:system)
- channel1 = create(:channel, codeplug: @codeplug, system: system, long_name: "Channel 1")
- channel2 = create(:channel, codeplug: @codeplug, system: system, long_name: "Channel 2")
- channel3 = create(:channel, codeplug: @codeplug, system: system, long_name: "Channel 3")
+ system1 = create(:system, :analog, name: "System 1")
+ system2 = create(:system, :analog, name: "System 2")
+ system3 = create(:system, :analog, name: "System 3")
- cz1 = create(:channel_zone, zone: @zone, channel: channel1, position: 1)
- cz2 = create(:channel_zone, zone: @zone, channel: channel2, position: 2)
- cz3 = create(:channel_zone, zone: @zone, channel: channel3, position: 3)
+ zs1 = create(:zone_system, zone: @zone, system: system1, position: 1)
+ zs2 = create(:zone_system, zone: @zone, system: system2, position: 2)
+ zs3 = create(:zone_system, zone: @zone, system: system3, position: 3)
- # Reorder: move channel3 to position 1
- patch update_positions_codeplug_zone_path(@codeplug, @zone), params: {
+ # Reorder: move system3 to position 1
+ patch update_positions_zone_path(@zone), params: {
positions: [
- { id: cz3.id, position: 1 },
- { id: cz1.id, position: 2 },
- { id: cz2.id, position: 3 }
+ { id: zs3.id, position: 1 },
+ { id: zs1.id, position: 2 },
+ { id: zs2.id, position: 3 }
]
}, as: :json
assert_response :success
# Verify positions updated
- assert_equal 1, cz3.reload.position
- assert_equal 2, cz1.reload.position
- assert_equal 3, cz2.reload.position
+ assert_equal 1, zs3.reload.position
+ assert_equal 2, zs1.reload.position
+ assert_equal 3, zs2.reload.position
end
test "should not update positions for other user's zone" do
log_in_as(@user)
- system = create(:system)
- channel = create(:channel, codeplug: @other_codeplug, system: system)
- cz = create(:channel_zone, zone: @other_zone, channel: channel, position: 1)
+ system = create(:system, :analog)
+ zs = create(:zone_system, zone: @other_zone, system: system, position: 1)
- patch update_positions_codeplug_zone_path(@other_codeplug, @other_zone), params: {
+ patch update_positions_zone_path(@other_zone), params: {
positions: [
- { id: cz.id, position: 2 }
+ { id: zs.id, position: 2 }
]
}, as: :json
- assert_redirected_to codeplugs_path
+ assert_response :forbidden
end
test "should require login for update_positions" do
- patch update_positions_codeplug_zone_path(@codeplug, @zone), params: {
+ patch update_positions_zone_path(@zone), params: {
positions: []
}, as: :json
assert_redirected_to login_path
end
-
- # Standalone Zones Routes Tests (new top-level resource)
- # These tests are for the new standalone zones functionality (not nested under codeplugs)
-
- test "standalone index should show user's zones and public zones" do
- log_in_as(@user)
-
- # Create test data
- my_public_zone = create(:zone, user: @user, public: true, name: "My Public Zone")
- my_private_zone = create(:zone, user: @user, public: false, name: "My Private Zone")
- other_public_zone = create(:zone, user: @other_user, public: true, name: "Other Public Zone")
- other_private_zone = create(:zone, user: @other_user, public: false, name: "Other Private Zone")
-
- get zones_path
-
- assert_response :success
- # Should see own zones (both public and private)
- assert_select "body", text: /My Public Zone/
- assert_select "body", text: /My Private Zone/
- # Should see other users' public zones
- assert_select "body", text: /Other Public Zone/
- # Should NOT see other users' private zones
- assert_select "body", { text: /Other Private Zone/, count: 0 }
- end
-
- test "standalone index should require login" do
- get zones_path
- assert_redirected_to login_path
- end
-
- test "standalone show should display public zone to any user" do
- log_in_as(@user)
- public_zone = create(:zone, user: @other_user, public: true, name: "Public Zone")
-
- get zone_path(public_zone)
-
- assert_response :success
- assert_select "h1", "Public Zone"
- end
-
- test "standalone show should display own private zone" do
- log_in_as(@user)
- my_private_zone = create(:zone, user: @user, public: false, name: "My Private Zone")
-
- get zone_path(my_private_zone)
-
- assert_response :success
- assert_select "h1", "My Private Zone"
- end
-
- test "standalone show should not display other user's private zone" do
- log_in_as(@user)
- other_private_zone = create(:zone, user: @other_user, public: false, name: "Private Zone")
-
- get zone_path(other_private_zone)
-
- assert_response :forbidden
- end
-
- test "standalone show should require login" do
- public_zone = create(:zone, user: @user, public: true)
-
- get zone_path(public_zone)
-
- assert_redirected_to login_path
- end
-
- test "standalone new should require login" do
- get new_zone_path
- assert_redirected_to login_path
- end
-
- test "standalone create should create zone for logged in user" do
- log_in_as(@user)
-
- assert_difference("Zone.count", 1) do
- post zones_path, params: {
- zone: {
- name: "New Standalone Zone",
- long_name: "New Standalone Zone Long",
- short_name: "NSZ",
- public: false
- }
- }
- end
-
- zone = Zone.last
- assert_equal @user, zone.user
- assert_equal "New Standalone Zone", zone.name
- assert_redirected_to zone_path(zone)
- end
-
- test "standalone create should require login" do
- assert_no_difference("Zone.count") do
- post zones_path, params: {
- zone: {
- name: "New Zone"
- }
- }
- end
-
- assert_redirected_to login_path
- end
-
- test "standalone update should update own zone" do
- log_in_as(@user)
- my_zone = create(:zone, user: @user, name: "Original Name")
-
- patch zone_path(my_zone), params: {
- zone: {
- name: "Updated Name"
- }
- }
-
- assert_equal "Updated Name", my_zone.reload.name
- assert_redirected_to zone_path(my_zone)
- end
-
- test "standalone update should not update other user's zone" do
- log_in_as(@user)
- other_zone = create(:zone, user: @other_user, name: "Original Name")
-
- patch zone_path(other_zone), params: {
- zone: {
- name: "Hacked Name"
- }
- }
-
- assert_equal "Original Name", other_zone.reload.name
- assert_response :forbidden
- end
-
- test "standalone destroy should delete own zone" do
- log_in_as(@user)
- my_zone = create(:zone, user: @user)
-
- assert_difference("Zone.count", -1) do
- delete zone_path(my_zone)
- end
-
- assert_redirected_to zones_path
- end
-
- test "standalone destroy should not delete other user's zone" do
- log_in_as(@user)
- other_zone = create(:zone, user: @other_user)
-
- assert_no_difference("Zone.count") do
- delete zone_path(other_zone)
- end
-
- assert_response :forbidden
- end
end
diff --git a/test/factories/codeplug_zones.rb b/test/factories/codeplug_zones.rb
index 4493850..bbd2225 100644
--- a/test/factories/codeplug_zones.rb
+++ b/test/factories/codeplug_zones.rb
@@ -3,10 +3,5 @@
association :codeplug
association :zone
sequence(:position) { |n| n }
-
- # Ensure zone belongs to the same codeplug
- after(:build) do |cz|
- cz.zone.codeplug = cz.codeplug if cz.codeplug && cz.zone
- end
end
end
diff --git a/test/factories/zones.rb b/test/factories/zones.rb
index 9de62a5..40365d9 100644
--- a/test/factories/zones.rb
+++ b/test/factories/zones.rb
@@ -1,7 +1,6 @@
FactoryBot.define do
factory :zone do
association :user
- association :codeplug
name { Faker::Lorem.words(number: 2).join(" ").titleize }
long_name { Faker::Lorem.words(number: 4).join(" ").titleize }
short_name { Faker::Alphanumeric.alpha(number: 3).upcase }
diff --git a/test/models/codeplug_test.rb b/test/models/codeplug_test.rb
index b45e83b..302a3b9 100644
--- a/test/models/codeplug_test.rb
+++ b/test/models/codeplug_test.rb
@@ -54,15 +54,22 @@ class CodeplugTest < ActiveSupport::TestCase
assert_equal :belongs_to, association.macro
end
- test "should have many zones" do
+ test "should have many zones through codeplug_zones" do
codeplug = create(:codeplug)
assert_respond_to codeplug, :zones
end
- test "zones association should be configured with dependent destroy" do
+ test "zones association should be configured as through" do
association = Codeplug.reflect_on_association(:zones)
assert_not_nil association, "zones association should exist"
assert_equal :has_many, association.macro
+ assert_equal :codeplug_zones, association.options[:through]
+ end
+
+ test "should have many codeplug_zones with dependent destroy" do
+ association = Codeplug.reflect_on_association(:codeplug_zones)
+ assert_not_nil association, "codeplug_zones association should exist"
+ assert_equal :has_many, association.macro
assert_equal :destroy, association.options[:dependent]
end
@@ -79,11 +86,17 @@ class CodeplugTest < ActiveSupport::TestCase
end
# Dependent Destroy Tests
- test "destroying codeplug should destroy associated zones" do
- codeplug = create(:codeplug)
- # Note: Zones will be created in a later issue, so this test will fail until then
- # For now, we're just testing the association configuration exists
- skip "Zone model not yet implemented"
+ test "destroying codeplug should destroy associated codeplug_zones" do
+ user = create(:user)
+ codeplug = create(:codeplug, user: user)
+ zone = create(:zone, user: user)
+ codeplug_zone = create(:codeplug_zone, codeplug: codeplug, zone: zone, position: 1)
+
+ assert_difference("CodeplugZone.count", -1) do
+ codeplug.destroy!
+ end
+ # Zone itself should still exist (it's standalone)
+ assert Zone.exists?(zone.id)
end
test "destroying codeplug should destroy associated channels" do
diff --git a/test/models/codeplug_zone_test.rb b/test/models/codeplug_zone_test.rb
index 9180d10..73698ba 100644
--- a/test/models/codeplug_zone_test.rb
+++ b/test/models/codeplug_zone_test.rb
@@ -73,8 +73,9 @@ class CodeplugZoneTest < ActiveSupport::TestCase
# Uniqueness Tests
test "should not save codeplug_zone with duplicate zone in same codeplug" do
- codeplug = create(:codeplug)
- zone = create(:zone, codeplug: codeplug)
+ user = create(:user)
+ codeplug = create(:codeplug, user: user)
+ zone = create(:zone, user: user)
create(:codeplug_zone, codeplug: codeplug, zone: zone, position: 1)
duplicate = build(:codeplug_zone, codeplug: codeplug, zone: zone, position: 2)
@@ -83,21 +84,22 @@ class CodeplugZoneTest < ActiveSupport::TestCase
end
test "should save codeplug_zone with same zone in different codeplugs" do
- codeplug1 = create(:codeplug)
- codeplug2 = create(:codeplug)
- zone1 = create(:zone, codeplug: codeplug1)
- zone2 = create(:zone, codeplug: codeplug2)
+ user = create(:user)
+ codeplug1 = create(:codeplug, user: user)
+ codeplug2 = create(:codeplug, user: user)
+ zone = create(:zone, user: user)
- cz1 = create(:codeplug_zone, codeplug: codeplug1, zone: zone1, position: 1)
- cz2 = build(:codeplug_zone, codeplug: codeplug2, zone: zone2, position: 1)
+ cz1 = create(:codeplug_zone, codeplug: codeplug1, zone: zone, position: 1)
+ cz2 = build(:codeplug_zone, codeplug: codeplug2, zone: zone, position: 1)
- assert cz2.save, "Failed to save same zone in different codeplug"
+ assert cz2.save, "Failed to save same zone in different codeplugs"
end
test "should not save codeplug_zone with duplicate position in same codeplug" do
- codeplug = create(:codeplug)
- zone1 = create(:zone, codeplug: codeplug, name: "Zone 1")
- zone2 = create(:zone, codeplug: codeplug, name: "Zone 2")
+ user = create(:user)
+ codeplug = create(:codeplug, user: user)
+ zone1 = create(:zone, user: user, name: "Zone 1")
+ zone2 = create(:zone, user: user, name: "Zone 2")
create(:codeplug_zone, codeplug: codeplug, zone: zone1, position: 1)
duplicate = build(:codeplug_zone, codeplug: codeplug, zone: zone2, position: 1)
@@ -106,10 +108,11 @@ class CodeplugZoneTest < ActiveSupport::TestCase
end
test "should save codeplug_zone with same position in different codeplugs" do
- codeplug1 = create(:codeplug)
- codeplug2 = create(:codeplug)
- zone1 = create(:zone, codeplug: codeplug1)
- zone2 = create(:zone, codeplug: codeplug2)
+ user = create(:user)
+ codeplug1 = create(:codeplug, user: user)
+ codeplug2 = create(:codeplug, user: user)
+ zone1 = create(:zone, user: user)
+ zone2 = create(:zone, user: user)
cz1 = create(:codeplug_zone, codeplug: codeplug1, zone: zone1, position: 1)
cz2 = build(:codeplug_zone, codeplug: codeplug2, zone: zone2, position: 1)
@@ -125,10 +128,11 @@ class CodeplugZoneTest < ActiveSupport::TestCase
# Multiple CodeplugZones per Codeplug
test "codeplug can have multiple codeplug_zones at different positions" do
- codeplug = create(:codeplug)
- zone1 = create(:zone, codeplug: codeplug, name: "Zone 1")
- zone2 = create(:zone, codeplug: codeplug, name: "Zone 2")
- zone3 = create(:zone, codeplug: codeplug, name: "Zone 3")
+ user = create(:user)
+ codeplug = create(:codeplug, user: user)
+ zone1 = create(:zone, user: user, name: "Zone 1")
+ zone2 = create(:zone, user: user, name: "Zone 2")
+ zone3 = create(:zone, user: user, name: "Zone 3")
cz1 = create(:codeplug_zone, codeplug: codeplug, zone: zone1, position: 1)
cz2 = create(:codeplug_zone, codeplug: codeplug, zone: zone2, position: 2)
@@ -142,10 +146,11 @@ class CodeplugZoneTest < ActiveSupport::TestCase
# Default Scope - Ordering by Position
test "codeplug_zones should be ordered by position by default" do
- codeplug = create(:codeplug)
- zone1 = create(:zone, codeplug: codeplug, name: "Zone 1")
- zone2 = create(:zone, codeplug: codeplug, name: "Zone 2")
- zone3 = create(:zone, codeplug: codeplug, name: "Zone 3")
+ user = create(:user)
+ codeplug = create(:codeplug, user: user)
+ zone1 = create(:zone, user: user, name: "Zone 1")
+ zone2 = create(:zone, user: user, name: "Zone 2")
+ zone3 = create(:zone, user: user, name: "Zone 3")
# Create in non-sequential order
cz3 = create(:codeplug_zone, codeplug: codeplug, zone: zone3, position: 3)
@@ -158,9 +163,10 @@ class CodeplugZoneTest < ActiveSupport::TestCase
# Through Association Tests
test "codeplug should have zones through codeplug_zones" do
- codeplug = create(:codeplug)
- zone1 = create(:zone, codeplug: codeplug, name: "Zone 1")
- zone2 = create(:zone, codeplug: codeplug, name: "Zone 2")
+ user = create(:user)
+ codeplug = create(:codeplug, user: user)
+ zone1 = create(:zone, user: user, name: "Zone 1")
+ zone2 = create(:zone, user: user, name: "Zone 2")
create(:codeplug_zone, codeplug: codeplug, zone: zone1, position: 1)
create(:codeplug_zone, codeplug: codeplug, zone: zone2, position: 2)
@@ -171,9 +177,10 @@ class CodeplugZoneTest < ActiveSupport::TestCase
end
test "zone should have codeplugs through codeplug_zones" do
- zone = create(:zone)
- codeplug1 = create(:codeplug)
- codeplug2 = create(:codeplug)
+ user = create(:user)
+ zone = create(:zone, user: user)
+ codeplug1 = create(:codeplug, user: user)
+ codeplug2 = create(:codeplug, user: user)
create(:codeplug_zone, zone: zone, codeplug: codeplug1, position: 1)
create(:codeplug_zone, zone: zone, codeplug: codeplug2, position: 1)
diff --git a/test/models/zone_test.rb b/test/models/zone_test.rb
index 0a70f25..ec28955 100644
--- a/test/models/zone_test.rb
+++ b/test/models/zone_test.rb
@@ -7,11 +7,6 @@ class ZoneTest < ActiveSupport::TestCase
assert zone.save, "Failed to save zone with valid attributes"
end
- test "should save zone without codeplug (codeplug is now optional)" do
- zone = build(:zone, codeplug: nil)
- assert zone.save, "Failed to save zone without codeplug"
- end
-
test "should not save zone without user" do
zone = build(:zone, user: nil)
assert_not zone.save, "Saved zone without user"
@@ -35,17 +30,6 @@ class ZoneTest < ActiveSupport::TestCase
end
# Association Tests
- test "should belong to codeplug" do
- zone = build(:zone)
- assert_respond_to zone, :codeplug
- end
-
- test "codeplug association should be configured" do
- association = Zone.reflect_on_association(:codeplug)
- assert_not_nil association, "codeplug association should exist"
- assert_equal :belongs_to, association.macro
- end
-
test "should have many channel_zones" do
zone = create(:zone)
assert_respond_to zone, :channel_zones
@@ -70,12 +54,45 @@ class ZoneTest < ActiveSupport::TestCase
assert_equal :channel_zones, association.options[:through]
end
- # Dependent Destroy Tests
- test "destroying zone should destroy associated channel_zones" do
+ test "should have many codeplug_zones" do
zone = create(:zone)
- # Note: ChannelZone will be created in a later issue, so this test will fail until then
- # For now, we're just testing the association configuration exists
- skip "ChannelZone model not yet implemented"
+ assert_respond_to zone, :codeplug_zones
+ end
+
+ test "codeplug_zones association should be configured with dependent destroy" do
+ association = Zone.reflect_on_association(:codeplug_zones)
+ assert_not_nil association, "codeplug_zones association should exist"
+ assert_equal :has_many, association.macro
+ assert_equal :destroy, association.options[:dependent]
+ end
+
+ test "should have many codeplugs through codeplug_zones" do
+ zone = create(:zone)
+ assert_respond_to zone, :codeplugs
+ end
+
+ test "codeplugs association should be configured as through" do
+ association = Zone.reflect_on_association(:codeplugs)
+ assert_not_nil association, "codeplugs association should exist"
+ assert_equal :has_many, association.macro
+ assert_equal :codeplug_zones, association.options[:through]
+ end
+
+ test "should have many zone_systems" do
+ zone = create(:zone)
+ assert_respond_to zone, :zone_systems
+ end
+
+ test "zone_systems association should be configured with dependent destroy" do
+ association = Zone.reflect_on_association(:zone_systems)
+ assert_not_nil association, "zone_systems association should exist"
+ assert_equal :has_many, association.macro
+ assert_equal :destroy, association.options[:dependent]
+ end
+
+ test "should have many systems through zone_systems" do
+ zone = create(:zone)
+ assert_respond_to zone, :systems
end
# Attribute Tests
@@ -94,27 +111,6 @@ class ZoneTest < ActiveSupport::TestCase
assert_equal "RPT", zone.short_name
end
- test "should allow different zones with same name in different codeplugs" do
- codeplug1 = create(:codeplug)
- codeplug2 = create(:codeplug)
- zone1 = create(:zone, codeplug: codeplug1, name: "Zone A")
- zone2 = create(:zone, codeplug: codeplug2, name: "Zone A")
-
- assert zone1.persisted?
- assert zone2.persisted?
- end
-
- # Multiple Zones per Codeplug
- test "codeplug can have multiple zones" do
- codeplug = create(:codeplug)
- zone1 = create(:zone, codeplug: codeplug, name: "Zone 1")
- zone2 = create(:zone, codeplug: codeplug, name: "Zone 2")
-
- assert_equal 2, codeplug.zones.count
- assert_includes codeplug.zones, zone1
- assert_includes codeplug.zones, zone2
- end
-
# Name Length Tests (no validation, just storage)
test "should store long names" do
long_name = "A" * 100
@@ -270,10 +266,32 @@ class ZoneTest < ActiveSupport::TestCase
assert zone2.persisted?
end
- # Codeplug Optional Tests
- test "zone can exist without being associated to a codeplug" do
- zone = create(:zone, codeplug: nil)
- assert_nil zone.codeplug
- assert zone.persisted?
+ # Codeplug Relationship Tests (through CodeplugZone)
+ test "zone can be added to multiple codeplugs" do
+ user = create(:user)
+ zone = create(:zone, user: user)
+ codeplug1 = create(:codeplug, user: user)
+ codeplug2 = create(:codeplug, user: user)
+
+ create(:codeplug_zone, codeplug: codeplug1, zone: zone, position: 1)
+ create(:codeplug_zone, codeplug: codeplug2, zone: zone, position: 1)
+
+ assert_equal 2, zone.codeplugs.count
+ assert_includes zone.codeplugs, codeplug1
+ assert_includes zone.codeplugs, codeplug2
+ end
+
+ test "codeplug can have multiple zones" do
+ user = create(:user)
+ codeplug = create(:codeplug, user: user)
+ zone1 = create(:zone, user: user, name: "Zone 1")
+ zone2 = create(:zone, user: user, name: "Zone 2")
+
+ create(:codeplug_zone, codeplug: codeplug, zone: zone1, position: 1)
+ create(:codeplug_zone, codeplug: codeplug, zone: zone2, position: 2)
+
+ assert_equal 2, codeplug.zones.count
+ assert_includes codeplug.zones, zone1
+ assert_includes codeplug.zones, zone2
end
end
diff --git a/test/system/codeplugs_test.rb b/test/system/codeplugs_test.rb
index 05ad1e4..036104a 100644
--- a/test/system/codeplugs_test.rb
+++ b/test/system/codeplugs_test.rb
@@ -85,9 +85,13 @@ class CodeplugsTest < ApplicationSystemTestCase
user = create(:user, email: "test@example.com", password: "password123")
codeplug = create(:codeplug, user: user, name: "Test Codeplug")
- # Create some zones and channels
- zone1 = create(:zone, codeplug: codeplug, long_name: "Zone 1")
- zone2 = create(:zone, codeplug: codeplug, long_name: "Zone 2")
+ # Create standalone zones and add to codeplug via CodeplugZone
+ zone1 = create(:zone, user: user, name: "Zone 1", long_name: "Zone 1")
+ zone2 = create(:zone, user: user, name: "Zone 2", long_name: "Zone 2")
+ create(:codeplug_zone, codeplug: codeplug, zone: zone1, position: 1)
+ create(:codeplug_zone, codeplug: codeplug, zone: zone2, position: 2)
+
+ # Create channels
system = create(:system, mode: "dmr")
channel1 = create(:channel, codeplug: codeplug, system: system, long_name: "Channel 1")
channel2 = create(:channel, codeplug: codeplug, system: system, long_name: "Channel 2")
diff --git a/test/system/zones_test.rb b/test/system/zones_test.rb
index 5b10d26..5dd41c7 100644
--- a/test/system/zones_test.rb
+++ b/test/system/zones_test.rb
@@ -1,227 +1,7 @@
require "application_system_test_case"
class ZonesTest < ApplicationSystemTestCase
- test "creating a new zone" do
- user = create(:user, email: "test@example.com", password: "password123")
- codeplug = create(:codeplug, user: user, name: "Test Codeplug")
-
- visit codeplug_path(codeplug)
- fill_in "Email", with: "test@example.com"
- fill_in "Password", with: "password123"
- click_button "Log In"
-
- click_link "Manage Zones"
- click_link "New Zone"
-
- fill_in "Name", with: "Zone 1"
- fill_in "Long name", with: "Local Repeaters"
- fill_in "Short name", with: "LCL"
- click_button "Create Zone"
-
- assert_text "Zone was successfully created"
- assert_text "Zone 1"
- assert_text "Local Repeaters"
- assert_text "LCL"
- end
-
- test "editing a zone" do
- user = create(:user, email: "test@example.com", password: "password123")
- codeplug = create(:codeplug, user: user, name: "Test Codeplug")
- zone = create(:zone, codeplug: codeplug, name: "Original Zone")
-
- visit codeplug_zones_path(codeplug)
- fill_in "Email", with: "test@example.com"
- fill_in "Password", with: "password123"
- click_button "Log In"
-
- click_link "Edit", match: :first
-
- fill_in "Name", with: "Updated Zone"
- fill_in "Long name", with: "Updated Long Name"
- click_button "Update Zone"
-
- assert_text "Zone was successfully updated"
- assert_text "Updated Zone"
- assert_text "Updated Long Name"
- end
-
- test "viewing zones index" do
- user = create(:user, email: "test@example.com", password: "password123")
- codeplug = create(:codeplug, user: user, name: "Test Codeplug")
- zone1 = create(:zone, codeplug: codeplug, name: "Zone 1", long_name: "Zone One")
- zone2 = create(:zone, codeplug: codeplug, name: "Zone 2", long_name: "Zone Two")
-
- visit codeplug_zones_path(codeplug)
- fill_in "Email", with: "test@example.com"
- fill_in "Password", with: "password123"
- click_button "Log In"
-
- assert_text "Zone 1"
- assert_text "Zone 2"
- assert_text "Zone One"
- assert_text "Zone Two"
- end
-
- test "viewing zone details" do
- user = create(:user, email: "test@example.com", password: "password123")
- codeplug = create(:codeplug, user: user, name: "Test Codeplug")
- zone = create(:zone, codeplug: codeplug, name: "Test Zone", long_name: "Test Long Name")
-
- visit codeplug_zone_path(codeplug, zone)
- fill_in "Email", with: "test@example.com"
- fill_in "Password", with: "password123"
- click_button "Log In"
-
- assert_text "Test Zone"
- assert_text "Test Long Name"
- end
-
- test "empty state shows helpful message" do
- user = create(:user, email: "test@example.com", password: "password123")
- codeplug = create(:codeplug, user: user, name: "Test Codeplug")
-
- visit codeplug_zones_path(codeplug)
- fill_in "Email", with: "test@example.com"
- fill_in "Password", with: "password123"
- click_button "Log In"
-
- assert_text "No zones found"
- assert_link "Create the first one"
- end
-
- test "manage zones link from codeplug show page" do
- user = create(:user, email: "test@example.com", password: "password123")
- codeplug = create(:codeplug, user: user, name: "Test Codeplug")
-
- visit codeplug_path(codeplug)
- fill_in "Email", with: "test@example.com"
- fill_in "Password", with: "password123"
- click_button "Log In"
-
- click_link "Manage Zones"
-
- assert_current_path codeplug_zones_path(codeplug)
- assert_text "Zones"
- end
-
- test "cannot access other user's zones" do
- user = create(:user, email: "test@example.com", password: "password123")
- other_user = create(:user)
- other_codeplug = create(:codeplug, user: other_user, name: "Other Codeplug")
-
- visit codeplug_zones_path(other_codeplug)
- fill_in "Email", with: "test@example.com"
- fill_in "Password", with: "password123"
- click_button "Log In"
-
- assert_text "You don't have permission to access this codeplug"
- assert_current_path codeplugs_path
- end
-
- test "zone shows channels with drag handles" do
- user = create(:user, email: "test@example.com", password: "password123")
- codeplug = create(:codeplug, user: user, name: "Test Codeplug")
- zone = create(:zone, codeplug: codeplug, name: "Test Zone")
- system = create(:system)
- channel1 = create(:channel, codeplug: codeplug, system: system, long_name: "Channel 1")
- channel2 = create(:channel, codeplug: codeplug, system: system, long_name: "Channel 2")
- create(:channel_zone, zone: zone, channel: channel1, position: 1)
- create(:channel_zone, zone: zone, channel: channel2, position: 2)
-
- visit codeplug_zone_path(codeplug, zone)
- fill_in "Email", with: "test@example.com"
- fill_in "Password", with: "password123"
- click_button "Log In"
-
- assert_text "Channel 1"
- assert_text "Channel 2"
- assert_text "2 channels"
-
- # Verify drag handles are present (via Bootstrap icon SVG)
- assert_selector ".drag-handle", count: 2
- assert_selector ".list-group-item[data-id]", count: 2
- end
-
- test "adding a channel to a zone" do
- user = create(:user, email: "test@example.com", password: "password123")
- codeplug = create(:codeplug, user: user, name: "Test Codeplug")
- zone = create(:zone, codeplug: codeplug, name: "Test Zone")
- system = create(:system)
- channel = create(:channel, codeplug: codeplug, system: system, long_name: "Test Channel")
-
- visit codeplug_zone_path(codeplug, zone)
- fill_in "Email", with: "test@example.com"
- fill_in "Password", with: "password123"
- click_button "Log In"
-
- # Verify the add channel form is present
- assert_selector "select#channel_zone_channel_id"
- assert_button "Add Channel"
-
- select "Test Channel", from: "channel_zone_channel_id"
- click_button "Add Channel"
-
- assert_text "Channel was successfully added to zone"
- assert_text "Test Channel"
- assert_text "1 channel"
- end
-
- # Skipping due to Turbo confirm dialog issues with Capybara
- # Controller tests cover the functionality
- test "removing a channel from a zone" do
- skip "Turbo confirm dialogs don't work reliably in system tests"
-
- user = create(:user, email: "test@example.com", password: "password123")
- codeplug = create(:codeplug, user: user, name: "Test Codeplug")
- zone = create(:zone, codeplug: codeplug, name: "Test Zone")
- system = create(:system)
- channel = create(:channel, codeplug: codeplug, system: system, long_name: "Test Channel")
- create(:channel_zone, zone: zone, channel: channel, position: 1)
-
- visit codeplug_zone_path(codeplug, zone)
- fill_in "Email", with: "test@example.com"
- fill_in "Password", with: "password123"
- click_button "Log In"
-
- assert_text "Test Channel"
- assert_text "1 channel"
-
- click_button "Remove"
-
- assert_text "Channel was successfully removed from zone"
- assert_text "No channels in this zone yet"
- end
-
- test "editing a channel from zone view" do
- user = create(:user, email: "test@example.com", password: "password123")
- codeplug = create(:codeplug, user: user, name: "Test Codeplug")
- zone = create(:zone, codeplug: codeplug, name: "Test Zone")
- system = create(:system)
- channel = create(:channel, codeplug: codeplug, system: system, long_name: "Original Name")
- create(:channel_zone, zone: zone, channel: channel, position: 1)
-
- visit codeplug_zone_path(codeplug, zone)
- fill_in "Email", with: "test@example.com"
- fill_in "Password", with: "password123"
- click_button "Log In"
-
- # Click the Edit button within the channel list (not the zone edit button)
- within(".list-group") do
- click_link "Edit"
- end
-
- assert_text "Edit Channel"
- fill_in "Long name", with: "Updated Name"
- click_button "Update Channel"
-
- assert_text "Channel was successfully updated"
- end
-
- # ========================================
- # Standalone Zones Tests (top-level resource)
- # ========================================
-
- test "user can navigate to standalone zones index from navbar" do
+ test "user can navigate to zones index from navbar" do
user = create(:user, email: "test@example.com", password: "password123")
create(:zone, user: user, name: "My Zone", public: false)
@@ -237,7 +17,7 @@ class ZonesTest < ApplicationSystemTestCase
assert_text "My Zone"
end
- test "standalone zones index shows user's zones and public zones" do
+ test "zones index shows user's zones and public zones" do
user = create(:user, email: "test@example.com", password: "password123")
other_user = create(:user, email: "other@example.com")
@@ -263,7 +43,7 @@ class ZonesTest < ApplicationSystemTestCase
assert_no_text "Other Private Zone"
end
- test "standalone zones index shows owner and visibility badges" do
+ test "zones index shows owner and visibility badges" do
user = create(:user, email: "test@example.com", password: "password123")
other_user = create(:user, email: "other@example.com")
@@ -275,7 +55,7 @@ class ZonesTest < ApplicationSystemTestCase
fill_in "Password", with: "password123"
click_button "Log In"
- # Verify the table headers for standalone zones
+ # Verify the table headers
assert_selector "th", text: "Owner"
assert_selector "th", text: "Visibility"
assert_selector "th", text: "Systems"
@@ -287,7 +67,7 @@ class ZonesTest < ApplicationSystemTestCase
assert_text "other@example.com" # Owner column for other user's zone
end
- test "user can view standalone zone details" do
+ test "user can view zone details" do
user = create(:user, email: "test@example.com", password: "password123")
zone = create(:zone, user: user, name: "Test Zone", long_name: "Test Long Name", public: false)
@@ -336,7 +116,7 @@ class ZonesTest < ApplicationSystemTestCase
assert_no_text "Other Private Zone"
end
- test "standalone zone shows systems with drag-drop reordering" do
+ test "zone shows systems with drag-drop reordering" do
user = create(:user, email: "test@example.com", password: "password123")
zone = create(:zone, user: user, name: "Test Zone")
system1 = create(:system, name: "System 1", rx_frequency: 145.0, tx_frequency: 145.6)
@@ -360,7 +140,7 @@ class ZonesTest < ApplicationSystemTestCase
assert_selector ".list-group-item[data-id]", count: 2
end
- test "adding a system to standalone zone" do
+ test "adding a system to zone" do
user = create(:user, email: "test@example.com", password: "password123")
zone = create(:zone, user: user, name: "Test Zone")
system = create(:system, name: "Test System", rx_frequency: 145.0, tx_frequency: 145.6)
@@ -382,7 +162,7 @@ class ZonesTest < ApplicationSystemTestCase
assert_text "1 system"
end
- test "standalone zone empty state shows helpful message" do
+ test "zone empty state shows helpful message" do
user = create(:user, email: "test@example.com", password: "password123")
zone = create(:zone, user: user, name: "Empty Zone")
@@ -394,7 +174,7 @@ class ZonesTest < ApplicationSystemTestCase
assert_text "No systems in this zone yet"
end
- test "creating a new standalone zone" do
+ test "creating a new zone" do
user = create(:user, email: "test@example.com", password: "password123")
visit new_zone_path
@@ -402,17 +182,17 @@ class ZonesTest < ApplicationSystemTestCase
fill_in "Password", with: "password123"
click_button "Log In"
- fill_in "Name", with: "New Standalone Zone"
+ fill_in "Name", with: "New Zone"
fill_in "Long name", with: "My New Zone Long Name"
- fill_in "Short name", with: "NSZ"
+ fill_in "Short name", with: "NZ"
click_button "Create Zone"
assert_text "Zone was successfully created"
- assert_text "New Standalone Zone"
+ assert_text "New Zone"
assert_text "My New Zone Long Name"
end
- test "editing standalone zone" do
+ test "editing zone" do
user = create(:user, email: "test@example.com", password: "password123")
zone = create(:zone, user: user, name: "Original Name")
@@ -430,6 +210,18 @@ class ZonesTest < ApplicationSystemTestCase
assert_text "Updated Long Name"
end
+ test "zones index empty state shows helpful message" do
+ user = create(:user, email: "test@example.com", password: "password123")
+
+ visit zones_path
+ fill_in "Email", with: "test@example.com"
+ fill_in "Password", with: "password123"
+ click_button "Log In"
+
+ assert_text "No zones found"
+ assert_link "Create the first one"
+ end
+
# Talkgroup selection tests for digital systems
test "digital system in zone shows talkgroup management" do
user = create(:user, email: "test@example.com", password: "password123")