Expand and tidy tests

* Update expectation style to match new Mocha requirements
* Clean up broken Startupper tests
* Flesh out various test stubs
* Clean up formatting and other odds and ends
This commit is contained in:
Eric B. Budd 2021-10-26 09:44:29 -05:00 committed by Eric Budd
parent 1c124349eb
commit 93b64d33a9
3 changed files with 149 additions and 94 deletions

View File

@ -19,7 +19,7 @@ Iris is strictly text-based, requiring no GUI or web servers.
## Installation ## Installation
At its core, Iris is simply a single, executable Ruby script. It has been tested and is known to work with Ruby 2.3.5. No extra gems or libraries are required. At its core, Iris is simply a single, executable Ruby script. It has been tested and is known to work with Ruby 2.3.5 and above. No extra gems or libraries are required.
Copy or symlink `iris.rb` somewhere the whole server can use it; `/usr/local/bin` is a good candidate: Copy or symlink `iris.rb` somewhere the whole server can use it; `/usr/local/bin` is a good candidate:
@ -616,6 +616,12 @@ gem install --user-install minitest
gem install --user-install mocha gem install --user-install mocha
``` ```
To run the tests:
```bash
ruby tests/iris_test.rb
```
## Technical Bits ## Technical Bits
* [Dependencies](#dependencies) * [Dependencies](#dependencies)

View File

@ -1037,7 +1037,7 @@ class Startupper
NONFILE_OPTIONS = %w[-h --help -v --version] NONFILE_OPTIONS = %w[-h --help -v --version]
def initialize(args) def initialize(args)
perform_file_checks unless NONFILE_OPTIONS.include?(args) Startupper.perform_file_checks unless NONFILE_OPTIONS.include?(args)
load_corpus(args) load_corpus(args)
@ -1052,8 +1052,7 @@ class Startupper
end end
end end
def perform_file_checks def self.perform_file_checks
raise 'Should not try to perform file checks in test mode!' if $test_corpus_file
unless File.exists?(Config::MESSAGE_FILE) unless File.exists?(Config::MESSAGE_FILE)
Display.say "You don't have a message file at #{Config::MESSAGE_FILE}." Display.say "You don't have a message file at #{Config::MESSAGE_FILE}."
response = Readline.readline 'Would you like me to create it for you? (y/n) ', true response = Readline.readline 'Would you like me to create it for you? (y/n) ', true
@ -1082,8 +1081,6 @@ class Startupper
end end
def load_corpus(args) def load_corpus(args)
$test_corpus_file = nil
if (args & %w{-f --test-file}).any? if (args & %w{-f --test-file}).any?
filename_idx = (args.index('-f') || args.index('--test-file')) + 1 filename_idx = (args.index('-f') || args.index('--test-file')) + 1
filename = args[filename_idx] filename = args[filename_idx]

View File

@ -1,37 +1,37 @@
require 'minitest/autorun' require 'minitest/autorun'
require 'mocha/mini_test' require 'mocha/minitest'
# This allows the test to pretend that user "jerryberry" is logged in.
ENV['USER'] = 'jerryberry'
# Set this before loading the code so that the Config constants load correctly.
$test_corpus_file = "./tests/iris.messages.json" $test_corpus_file = "./tests/iris.messages.json"
# Setting this before loading the main code file so that the Config contants
# load correctly. This will allows the test to pretend that user "jerryberry"
# is logged in.
ENV.stubs(:[]).returns('jerryberry')
require './iris.rb' require './iris.rb'
describe Config do describe Config do
it 'has the Iris semantic version number' do it 'has the Iris semantic version number' do
Config::VERSION.must_match /^\d\.\d\.\d+$/ _(Config::VERSION).must_match /^\d\.\d\.\d+$/
end end
it 'has the message file location' do it 'has the message file location' do
Config::MESSAGE_FILE.must_match /\/\.iris\.messages$/ _(Config::MESSAGE_FILE).must_match /\/\.iris\.messages$/
end end
it 'has the readline history file location' do it 'has the readline history file location' do
Config::HISTORY_FILE.must_match /\/\.iris\.history$/ _(Config::HISTORY_FILE).must_match /\/\.iris\.history$/
end end
it 'has the username' do it 'has the username' do
Config::USER.must_equal 'jerryberry' _(Config::USER).must_equal 'jerryberry'
end end
it 'has a hostname' do it 'has a hostname' do
Config::HOSTNAME.wont_be_nil _(Config::HOSTNAME).wont_be_nil
end end
it 'has the author' do it 'has the author' do
Config::AUTHOR.must_equal "#{Config::USER}@#{Config::HOSTNAME}" _(Config::AUTHOR).must_equal "#{Config::USER}@#{Config::HOSTNAME}"
end end
describe '.find_files' do describe '.find_files' do
@ -43,17 +43,21 @@ describe Config do
it 'returns a list of Iris message files' do it 'returns a list of Iris message files' do
Config.stubs(:`).returns("foo\nbar\n") Config.stubs(:`).returns("foo\nbar\n")
Config.find_files.must_equal ['foo', 'bar'] _(Config.find_files).must_equal ['foo', 'bar']
end end
it 'returns an empty array if no Iris message files are found' do it 'returns an empty array if no Iris message files are found' do
Config.stubs(:`).returns('') Config.stubs(:`).returns('')
Config.find_files.must_equal [] _(Config.find_files).must_equal []
end end
end end
end end
describe Corpus do describe Corpus do
before do
Corpus.load
end
describe '.load' do describe '.load' do
it 'loads all the message files' it 'loads all the message files'
it 'sets the corpus class variable' it 'sets the corpus class variable'
@ -62,10 +66,6 @@ describe Corpus do
it 'creates parent-hash-to-child-indexes index' it 'creates parent-hash-to-child-indexes index'
end end
describe '.all' do
it 'returns the entire corpus of messages'
end
describe '.topics' do describe '.topics' do
it 'returns all the messages which are topics' it 'returns all the messages which are topics'
it 'does not return reply messages' it 'does not return reply messages'
@ -78,28 +78,26 @@ describe Corpus do
describe '.find_message_by_hash' do describe '.find_message_by_hash' do
it 'returns nil if a nil is passed in' do it 'returns nil if a nil is passed in' do
Corpus.find_message_by_hash(nil).must_equal nil assert_nil Corpus.find_message_by_hash(nil)
end end
it 'returns nil if the hash is not found in the corpus' do it 'returns nil if the hash is not found in the corpus' do
# Corpus.load assert_nil Corpus.find_message_by_hash('NoofMcGoof')
Corpus.find_message_by_hash('NoofMcGoof').must_equal nil
end end
it 'returns the message associated with the hash if it is found' do it 'returns the message associated with the hash if it is found' do
Corpus.load message = Corpus.find_message_by_hash("gpY2WW/jGcH+BODgySCwDANJlIM=\n")
Corpus.find_message_by_hash("gpY2WW/jGcH+BODgySCwDANJlIM=").must_equal "Test" _(message.message).must_equal "Test"
end end
end end
describe '.find_all_by_parent_hash' do describe '.find_all_by_parent_hash' do
it 'returns an empty array if a nil is passed in' do it 'returns an empty array if a nil is passed in' do
Corpus.find_all_by_parent_hash(nil).must_equal [] _(Corpus.find_all_by_parent_hash(nil)).must_equal []
end end
it 'returns an empty array if the hash is not a parent of any other messages' do it 'returns an empty array if the hash is not a parent of any other messages' do
skip _(Corpus.find_all_by_parent_hash('GoofMcDoof')).must_equal []
Corpus.find_all_by_parent_hash('GoofMcDoof').must_equal []
end end
it 'returns an empty array if the hash is not found in the corpus' it 'returns an empty array if the hash is not found in the corpus'
@ -108,7 +106,7 @@ describe Corpus do
describe '.find_topic_by_id' do describe '.find_topic_by_id' do
it 'returns nil if a nil is passed in' do it 'returns nil if a nil is passed in' do
Corpus.find_topic_by_id(nil).must_equal nil assert_nil Corpus.find_topic_by_id(nil)
end end
describe 'when an index string is passed in' do describe 'when an index string is passed in' do
@ -119,7 +117,7 @@ describe Corpus do
describe '.find_topic_by_hash' do describe '.find_topic_by_hash' do
it 'returns nil if a nil is passed in' do it 'returns nil if a nil is passed in' do
Corpus.find_topic_by_hash(nil).must_equal nil assert_nil Corpus.find_topic_by_hash(nil)
end end
describe 'when a hash string is passed in' do describe 'when a hash string is passed in' do
@ -135,10 +133,6 @@ describe IrisFile do
end end
describe Message do describe Message do
it 'has a file version' do
Message::FILE_FORMAT.must_match /v\d/
end
it 'exposes all its data attributes for reading' it 'exposes all its data attributes for reading'
it 'is #valid? if it has no errors' it 'is #valid? if it has no errors'
@ -163,33 +157,44 @@ end
describe Display do describe Display do
it 'has a setting for a minimum width of 80' do it 'has a setting for a minimum width of 80' do
Display::MIN_WIDTH.must_equal 80 _(Display::MIN_WIDTH).must_equal 80
end end
it 'has a setting for the calculated screen width' it 'has a setting for a minimum height of 8' do
_(Display::MIN_HEIGHT).must_equal 8
end
it 'has settings for the calculated screen geometry' do
_(Display::WIDTH).wont_equal nil
_(Display::HEIGHT).wont_equal nil
end
describe '#topic_index_width' do describe '#topic_index_width' do
it 'returns the a minimun length of 2' do it 'returns the a minimun length of 2' do
Corpus.stubs(:topics).returns(%w{a}) Corpus.stubs(:topics).returns(%w{a})
Display.topic_index_width.must_equal 2 _(Display.topic_index_width).must_equal 2
end end
it 'returns the length in characters of the longest topic index' do it 'returns the length in characters of the longest topic index' do
Corpus.stubs(:topics).returns((0..1000).to_a) Corpus.stubs(:topics).returns((0..1000).to_a)
Display.topic_index_width.must_equal 4 _(Display.topic_index_width).must_equal 4
end end
it 'returns 2 if there are no topics' do it 'returns 2 if there are no topics' do
Corpus.stubs(:topics).returns([]) Corpus.stubs(:topics).returns([])
Display.topic_index_width.must_equal 2 _(Display.topic_index_width).must_equal 2
end end
end end
describe '#topic_author_width' do describe '#topic_author_width' do
it 'returns the length in characters of the longest author\'s name' it 'returns the length in characters of the longest author\'s name' do
Corpus.stubs(:authors).returns(['jerryberry@ctrl-c.club'])
_(Display.topic_author_width).must_equal 22
end
it 'returns 1 if there are no topics' do it 'returns 1 if there are no topics' do
Corpus.stubs(:topics).returns([]) Corpus.stubs(:authors).returns([])
Display.topic_author_width.must_equal 1 _(Display.topic_author_width).must_equal 1
end end
end end
@ -254,46 +259,6 @@ describe Startupper do
File.stubs(:stat).with(read_file_path).returns(data_file_stat) File.stubs(:stat).with(read_file_path).returns(data_file_stat)
Interface.stubs(:start) Interface.stubs(:start)
Display.stubs(:say)
end
it 'offers to create a message file if the user doesn\'t have one' do
File.stubs(:exists?).with(message_file_path).returns(false)
Readline.expects(:readline).with('Would you like me to create it for you? (y/n) ', true).returns('y')
IrisFile.expects(:create_message_file)
Startupper.new([])
end
it 'creates a read file if the user doesn\'t have one' do
File.stubs(:exists?).with(read_file_path).returns(false)
IrisFile.expects(:create_read_file)
Startupper.new([])
end
it 'warns the user if the message file permissions are wrong' do
skip
File.expects(:stat).with(message_file_path).returns(bad_file_stat)
Display.expects(:say).with('Your message file has incorrect permissions! Should be "-rw-r--r--".')
Startupper.new([])
end
it 'warns the user if the read file permissions are wrong' do
skip
File.stubs(:stat).with(read_file_path).returns(bad_file_stat)
Display.expects(:say).with('Your read file has incorrect permissions! Should be "-rw-r--r--".')
Startupper.new([])
end
it 'warns the user if the script file permissions are wrong' do
skip
File.expects(:stat).with(Config::IRIS_SCRIPT).returns(bad_file_stat)
Display.expects(:say).with('The Iris file has incorrect permissions! Should be "-rwxr-xr-x".')
Startupper.new([])
end end
it 'starts the Interface if no command-line arguments are provided' do it 'starts the Interface if no command-line arguments are provided' do
@ -315,11 +280,67 @@ describe Startupper do
CLI.expects(:start).with(['-h']) CLI.expects(:start).with(['-h'])
Startupper.new(['-h']) Startupper.new(['-h'])
end end
it 'offers to create a message file if the user doesn\'t have one' do
File.stubs(:exists?).with(message_file_path).returns(false)
Display.stubs(:say)
Readline.expects(:readline).with('Would you like me to create it for you? (y/n) ', true).returns('y')
IrisFile.expects(:create_message_file)
Startupper.new([])
end
it 'creates a read file if the user doesn\'t have one' do
File.stubs(:exists?).with(read_file_path).returns(false)
IrisFile.expects(:create_read_file)
Startupper.new([])
end
it 'warns the user if the message file permissions are wrong' do
File.expects(:stat).with(message_file_path).returns(bad_file_stat)
Display.stubs(:say)
message_lines = [
"Your message file has incorrect permissions! Should be \"-rw-r--r--\".",
"You can change this from the command line with:",
" chmod 644 jerryberry/.iris.messages",
"Leaving your file with incorrect permissions could allow unauthorized edits!"
]
Display.expects(:say).with(message_lines)
Startupper.new([])
end
it 'warns the user if the read file permissions are wrong' do
File.stubs(:stat).with(read_file_path).returns(bad_file_stat)
Display.stubs(:say)
message_lines = [
"Your read file has incorrect permissions! Should be \"-rw-r--r--\".",
"You can change this from the command line with:",
" chmod 644 jerryberry/.iris.read"
]
Display.expects(:say).with(message_lines)
Startupper.new([])
end
it 'warns the user if the script file permissions are wrong' do
File.expects(:stat).with(Config::IRIS_SCRIPT).returns(bad_file_stat)
Display.stubs(:say)
message_lines = [
"Your Iris file has incorrect permissions! Should be \"-rwxr-xr-x\".",
"You can change this from the command line with:",
" chmod 755 doots", "If this file has the wrong permissions the program may be tampered with!"
]
Display.expects(:say).with(message_lines)
Startupper.new([])
end
end end
end end
describe 'String#colorize' do describe 'String#colorize' do
let(:color_string) { let(:color_strings) {
" "
RED {r normal}\t{ri intense}\t{ru underline}\t{riu intense underline} RED {r normal}\t{ri intense}\t{ru underline}\t{riu intense underline}
{rv reverse}\t{riv intense}\t{ruv underline}\t{riuv intense underline} {rv reverse}\t{riv intense}\t{ruv underline}\t{riuv intense underline}
@ -335,28 +356,59 @@ describe 'String#colorize' do
{cv reverse}\t{civ intense}\t{cuv underline}\t{ciuv intense underline} {cv reverse}\t{civ intense}\t{cuv underline}\t{ciuv intense underline}
WHITE {w normal}\t{wi intense}\t{wu underline}\t{wiu intense underline} WHITE {w normal}\t{wi intense}\t{wu underline}\t{wiu intense underline}
{wv reverse}\t{wiv intense}\t{wuv underline}\t{wiuv intense underline} {wv reverse}\t{wiv intense}\t{wuv underline}\t{wiuv intense underline}
" ".split("\n")[1..-2]
} }
it 'produces the expected output' do it 'produces the expected output' do
skip lead = "\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m "
# color_string.split("\n")[1].colorize.must_equal "\n RED \e[31mnormal\e[0m\t\e[1;31mintense\e[0m\t\e[31;4munderline\e[0m\t\e[1;31;4mintense underline\e[0m\n \e[31;7mreverse\e[0m\t\e[1;31;7mintense\e[0m\t\e[31;4;7munderline\e[0m\t\e[1;31;4;7mintense underline\e[0m\n GREEN \e[32mnormal\e[0m\t\e[1;32mintense\e[0m\t\e[32;4munderline\e[0m\t\e[1;32;4mintense underline\e[0m\n \e[32;7mreverse\e[0m\t\e[1;32;7mintense\e[0m\t\e[32;4;7munderline\e[0m\t\e[1;32;4;7mintense underline\e[0m\n YELLOW \e[33mnormal\e[0m\t\e[1;33mintense\e[0m\t\e[33;4munderline\e[0m\t\e[1;33;4mintense underline\e[0m\n \e[33;7mreverse\e[0m\t\e[1;33;7mintense\e[0m\t\e[33;4;7munderline\e[0m\t\e[1;33;4;7mintense underline\e[0m\n BLUE \e[34mnormal\e[0m\t\e[1;34mintense\e[0m\t\e[34;4munderline\e[0m\t\e[1;34;4mintense underline\e[0m\n \e[34;7mreverse\e[0m\t\e[1;34;7mintense\e[0m\t\e[34;4;7munderline\e[0m\t\e[1;34;4;7mintense underline\e[0m\n MAGENTA \e[35mnormal\e[0m\t\e[1;35mintense\e[0m\t\e[35;4munderline\e[0m\t\e[1;35;4mintense underline\e[0m\n \e[35;7mreverse\e[0m\t\e[1;35;7mintense\e[0m\t\e[35;4;7munderline\e[0m\t\e[1;35;4;7mintense underline\e[0m\n CYAN \e[36mnormal\e[0m\t\e[1;36mintense\e[0m\t\e[36;4munderline\e[0m\t\e[1;36;4mintense underline\e[0m\n \e[36;7mreverse\e[0m\t\e[1;36;7mintense\e[0m\t\e[36;4;7munderline\e[0m\t\e[1;36;4;7mintense underline\e[0m\n WHITE \e[37mnormal\e[0m\t\e[1;37mintense\e[0m\t\e[37;4munderline\e[0m\t\e[1;37;4mintense underline\e[0m\n \e[37;7mreverse\e[0m\t\e[1;37;7mintense\e[0m\t\e[37;4;7munderline\e[0m\t\e[1;37;4;7mintense underline\e[0m\n \e[0m" lines = [
color_string.split("\n")[1].colorize.must_equal " RED \e[31mnormal\e[0m\t\e[1;31mintense\e[0m\t\e[31;4munderline\e[0m\t\e[1;31;4mintense underline\e[0m\e[0m" "RED \e[31mnormal\e[0m\t\e[1;31mintense\e[0m\t\e[31;4munderline\e[0m\t\e[1;31;4mintense underline\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m",
" \e[31;7mreverse\e[0m\t\e[1;31;7mintense\e[0m\t\e[31;4;7munderline\e[0m\t\e[1;31;4;7mintense underline\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m",
"GREEN \e[32mnormal\e[0m\t\e[1;32mintense\e[0m\t\e[32;4munderline\e[0m\t\e[1;32;4mintense underline\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m",
" \e[32;7mreverse\e[0m\t\e[1;32;7mintense\e[0m\t\e[32;4;7munderline\e[0m\t\e[1;32;4;7mintense underline\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m",
"YELLOW \e[33mnormal\e[0m\t\e[1;33mintense\e[0m\t\e[33;4munderline\e[0m\t\e[1;33;4mintense underline\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m",
" \e[33;7mreverse\e[0m\t\e[1;33;7mintense\e[0m\t\e[33;4;7munderline\e[0m\t\e[1;33;4;7mintense underline\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m",
"BLUE \e[34mnormal\e[0m\t\e[1;34mintense\e[0m\t\e[34;4munderline\e[0m\t\e[1;34;4mintense underline\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m",
" \e[34;7mreverse\e[0m\t\e[1;34;7mintense\e[0m\t\e[34;4;7munderline\e[0m\t\e[1;34;4;7mintense underline\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m",
"MAGENTA \e[35mnormal\e[0m\t\e[1;35mintense\e[0m\t\e[35;4munderline\e[0m\t\e[1;35;4mintense underline\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m",
" \e[35;7mreverse\e[0m\t\e[1;35;7mintense\e[0m\t\e[35;4;7munderline\e[0m\t\e[1;35;4;7mintense underline\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m",
"CYAN \e[36mnormal\e[0m\t\e[1;36mintense\e[0m\t\e[36;4munderline\e[0m\t\e[1;36;4mintense underline\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m",
" \e[36;7mreverse\e[0m\t\e[1;36;7mintense\e[0m\t\e[36;4;7munderline\e[0m\t\e[1;36;4;7mintense underline\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m",
"WHITE \e[37mnormal\e[0m\t\e[1;37mintense\e[0m\t\e[37;4munderline\e[0m\t\e[1;37;4mintense underline\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m",
" \e[37;7mreverse\e[0m\t\e[1;37;7mintense\e[0m\t\e[37;4;7munderline\e[0m\t\e[1;37;4;7mintense underline\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m\e[0m",
]
_(color_strings[0].colorize).must_equal lead + lines[0]
_(color_strings[1].colorize).must_equal lead + lines[1]
_(color_strings[2].colorize).must_equal lead + lines[2]
_(color_strings[3].colorize).must_equal lead + lines[3]
_(color_strings[4].colorize).must_equal lead + lines[4]
_(color_strings[5].colorize).must_equal lead + lines[5]
_(color_strings[6].colorize).must_equal lead + lines[6]
_(color_strings[7].colorize).must_equal lead + lines[7]
_(color_strings[8].colorize).must_equal lead + lines[8]
_(color_strings[9].colorize).must_equal lead + lines[9]
_(color_strings[10].colorize).must_equal lead + lines[10]
_(color_strings[11].colorize).must_equal lead + lines[11]
_(color_strings[12].colorize).must_equal lead + lines[12]
_(color_strings[13].colorize).must_equal lead + lines[13]
end end
it 'returns an empty string wrapped with resets when provided an empty string' do it 'returns an empty string wrapped with resets when provided an empty string' do
''.colorize.must_equal "\e[0m\e[0m" _(''.colorize).must_equal "\e[0m\e[0m"
end end
it 'allows curly brackets to be escaped' do it 'allows curly brackets to be escaped' do
'I want \{no color\}'.colorize.must_equal "\e[0m\e[0mI want {no color}\e[0m\e[0m\e[0m" _('I want \{no color\}'.colorize).must_equal "\e[0m\e[0mI want {no color}\e[0m\e[0m\e[0m"
end end
end end
describe 'String#decolorize' do describe 'String#decolorize' do
it 'returns the string with the coloring tags stripped' it 'returns the string with the coloring tags stripped' do
_("{b colorful}".decolorize).must_equal "colorful"
end
it 'allows curly brackets to be escaped' do it 'allows curly brackets to be escaped' do
'I want \{no color\}'.decolorize.must_equal "I want {no color}" _('I want \{no color\}'.decolorize).must_equal "I want {no color}"
end end
end end