diff --git a/bin/rubyripper_cli b/bin/rubyripper_cli
index a6dd0af..454f4f1 100755
--- a/bin/rubyripper_cli
+++ b/bin/rubyripper_cli
@@ -130,7 +130,7 @@ private
@out.puts _("Thanks for using RubyRipperRemix.")
@out.puts _("Have a nice day!")
when 1 then @cliPrefs.show()
- when 2 then @cliDisc.show()
+ when 2 then @cliDisc.show() ; showError(@cliDisc.error) unless @cliDisc.error.nil? ; @cliTracklist = CliTracklist.new(@cliDisc, @out, @int)
when 3 then @cliDisc.changeMetadata()
when 4 then @cliTracklist.show()
when 5 then startRip()
diff --git a/spec/cli/cliTracklist_spec.rb b/spec/cli/cliTracklist_spec.rb
new file mode 100644
index 0000000..0c7028f
--- /dev/null
+++ b/spec/cli/cliTracklist_spec.rb
@@ -0,0 +1,60 @@
+#!/usr/bin/env ruby
+# RubyRipperRemix - A secure ripper for Linux/BSD/OSX
+# Copyright (C) 2026 Masterisk-F
+#
+# This file is part of RubyRipperRemix. RubyRipperRemix is free software: you can
+# redistribute it and/or modify it under the terms of the GNU General
+# Public License as published by the Free Software Foundation, either
+# version 3 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+
+require 'rubyripper/cli/cliTracklist'
+
+describe CliTracklist do
+ let(:out) {StringIO.new}
+ let(:int) {double('CliGetInt').as_null_object}
+ let(:prefs) {double('Preferences::Main').as_null_object}
+
+ context "When selection is requested without prior user interaction" do
+ it "should return all tracks from the disc" do
+ cli_disc = double('CliDisc').as_null_object
+ expect(cli_disc).to receive(:tracks).and_return({1 => 'Track 1', 2 => 'Track 2', 3 => 'Track 3'})
+ tracklist = CliTracklist.new(cli_disc, out, int, prefs)
+ expect(tracklist.selection).to eq([1, 2, 3])
+ end
+
+ it "should lazily cache the selection after first access" do
+ cli_disc = double('CliDisc').as_null_object
+ expect(cli_disc).to receive(:tracks).once.and_return({1 => 'a', 2 => 'b'})
+ tracklist = CliTracklist.new(cli_disc, out, int, prefs)
+ expect(tracklist.selection).to eq([1, 2])
+ # second call should use cache, not call tracks again
+ expect(tracklist.selection).to eq([1, 2])
+ end
+ end
+
+ context "When the disc is rescanned" do
+ it "should get fresh track selection from the new disc when CliTracklist is recreated" do
+ cli_disc = double('CliDisc').as_null_object
+
+ # First disc has 5 tracks
+ expect(cli_disc).to receive(:tracks).once.and_return({1 => 'Song A', 2 => 'Song B', 3 => 'Song C', 4 => 'Song D', 5 => 'Song E'})
+ first_tracklist = CliTracklist.new(cli_disc, out, int, prefs)
+ expect(first_tracklist.selection).to eq([1, 2, 3, 4, 5])
+
+ # Simulate a rescan: CliDisc.tracks returns only 3 tracks now
+ expect(cli_disc).to receive(:tracks).once.and_return({1 => 'New A', 2 => 'New B', 3 => 'New C'})
+ second_tracklist = CliTracklist.new(cli_disc, out, int, prefs)
+
+ # The new instance should start fresh with all tracks from the new disc
+ expect(second_tracklist.selection).to eq([1, 2, 3])
+ end
+ end
+end
diff --git a/spec/cli/commandLineInterface_spec.rb b/spec/cli/commandLineInterface_spec.rb
new file mode 100644
index 0000000..9e4ceec
--- /dev/null
+++ b/spec/cli/commandLineInterface_spec.rb
@@ -0,0 +1,64 @@
+#!/usr/bin/env ruby
+# RubyRipperRemix - A secure ripper for Linux/BSD/OSX
+# Copyright (C) 2026 Masterisk-F
+#
+# This file is part of RubyRipperRemix. RubyRipperRemix is free software: you can
+# redistribute it and/or modify it under the terms of the GNU General
+# Public License as published by the Free Software Foundation, either
+# version 3 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+
+require 'rubyripper/base'
+require 'rubyripper/errors'
+require 'rubyripper/cli/cliArguments'
+require 'rubyripper/cli/cliDisc'
+require 'rubyripper/cli/cliTracklist'
+
+# CommandLineInterface is defined in bin/rubyripper_cli,
+# which is not in the lib load path. Load it by explicit path.
+# Load by absolute path: require/require_relative cannot resolve files without .rb extension.
+load File.expand_path('../../bin/rubyripper_cli', File.dirname(__FILE__))
+
+describe CommandLineInterface do
+ let(:out) {StringIO.new}
+ let(:int) {double('CliGetInt').as_null_object}
+ let(:cli_disc) {double('CliDisc').as_null_object}
+ let(:cli_prefs) {double('CliPreferences').as_null_object}
+
+ before do
+ # CliTracklist.new (called during option 2 at line 133) resolves
+ # Preferences::Main.instance when prefs is not injected.
+ # In spec mode ($run_specs = true), Singleton is excluded, so define instance here.
+ prefs_instance = double('PreferencesMain').as_null_object
+ allow(prefs_instance).to receive(:image).and_return(false)
+ allow(Preferences::Main).to receive(:instance).and_return(prefs_instance)
+ end
+
+ def setup_cli(cli_disc: nil, int: nil, prefs: nil)
+ CommandLineInterface.new(nil, out, prefs || cli_prefs, cli_disc || self.cli_disc, int || self.int)
+ end
+
+ context "When option 2 (rescan) is selected with a scan error" do
+ it "should display the scan error" do
+ # Stub @int to return 2 (rescan) then 99 (exit main menu)
+ expect(int).to receive(:get).with("Please type the number of your choice", 99)
+ .and_return(2, 99)
+
+ # CliDisc with non-nil error (simulating a failed scan)
+ allow(cli_disc).to receive(:error).and_return([:noDiscYet, '3'])
+ allow(cli_disc).to receive(:tracks).and_return({1 => 'Track 1'})
+ allow(cli_disc).to receive(:cd).and_return(double('Disc').as_null_object)
+
+ cli = setup_cli(cli_disc: cli_disc, int: int)
+
+ expect { cli.send(:loopMainMenu) }.to output(/Error/).to_stdout
+ end
+ end
+end