diff --git a/.rspec b/.rspec deleted file mode 100644 index 34c5164..0000000 --- a/.rspec +++ /dev/null @@ -1,3 +0,0 @@ ---format documentation ---color ---require spec_helper diff --git a/.rubocop.yml b/.rubocop.yml index a44b5f7..4e52d7f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,11 +1,39 @@ --- plugins: + - rubocop-minitest + - rubocop-performance - rubocop-rake - - rubocop-rspec AllCops: TargetRubyVersion: 3.4.0 NewCops: enable + SuggestExtensions: false + +Metrics/ClassLength: + CountAsOne: ['array', 'heredoc', 'hash'] + Max: 115 + +Metrics/ModuleLength: + CountAsOne: ['array', 'heredoc', 'hash'] + Max: 115 + Exclude: + - 'spec/**/*.rb' + +Metrics/MethodLength: + CountAsOne: ['array', 'heredoc', 'hash'] + +Metrics/BlockLength: + CountAsOne: ['array', 'heredoc', 'hash'] + Exclude: + - 'test/**/*' + - '*.gemspec' + +Layout/LineLength: + Max: 120 + AllowedPatterns: ['\A#'] + +Style/CommentedKeyword: + Enabled: false Style/StringLiterals: Enabled: true @@ -14,6 +42,3 @@ Style/StringLiterals: Style/StringLiteralsInInterpolation: Enabled: true EnforcedStyle: double_quotes - -Layout/LineLength: - Max: 120 diff --git a/Gemfile b/Gemfile index 14117ac..7880ceb 100644 --- a/Gemfile +++ b/Gemfile @@ -9,11 +9,13 @@ group :development, :test do gem 'changelog-rb', '~> 0.3' gem 'cucumber', '~> 9.1' gem 'gem-release', '~> 2.2' + gem 'minitest', '~> 5.0' gem 'rake', '~> 13.0' - gem 'rspec', '~> 3.0' gem 'rubocop', '~> 1.21' + gem 'rubocop-minitest', require: false + gem 'rubocop-performance', require: false gem 'rubocop-rake', require: false - gem 'rubocop-rspec', require: false + gem 'shoulda-context', '~> 2.0' end # Specify your gem's dependencies in linear-cli.gemspec diff --git a/Rakefile b/Rakefile index 4964751..10dc114 100644 --- a/Rakefile +++ b/Rakefile @@ -1,12 +1,21 @@ # frozen_string_literal: true require 'bundler/gem_tasks' -require 'rspec/core/rake_task' +require 'rake/testtask' -RSpec::Core::RakeTask.new(:spec) +Rake::TestTask.new(:test) do |t| + t.libs << 'test' + t.libs << 'lib' + t.test_files = FileList['test/**/*_test.rb'] +end require 'rubocop/rake_task' RuboCop::RakeTask.new -task default: %i[spec rubocop] +desc 'Run Cucumber features' +task :cucumber do + sh 'cucumber --format pretty' +end + +task default: %i[rubocop test cucumber] diff --git a/exe/scripts/lc.sh b/exe/scripts/lc.sh index 7f8a209..42485ba 100755 --- a/exe/scripts/lc.sh +++ b/exe/scripts/lc.sh @@ -29,7 +29,6 @@ elif [ $result -gt 1 ]; then printf "\n\nlc: linear-cli interrupted\n" >&2 exit 130 fi - printf "lc: linear-cli failed %s\n" $result >&2 - lc "$@" --help 2>&1 - exit 1 + exit $result fi +exit $result diff --git a/features/completion.feature b/features/completion.feature index 735bf96..e8ae876 100644 --- a/features/completion.feature +++ b/features/completion.feature @@ -6,10 +6,7 @@ Feature: Completion # TODO: Make this not raise an exception and just show the usage instead Scenario: Showing exception when no shell is given When I run `lc completion` - Then the output should contain: - """ - missing keyword: :shell (ArgumentError) - """ + Then the output should match /missing keyword: :shell \(ArgumentError\)/ Scenario: Showing exception when an invalid shell is given When I run `lc completion invalid` @@ -22,5 +19,9 @@ Feature: Completion When I run `lc completion bash` Then the output should contain: """ - # lc completion -*- shell-script -*- + # linear-cli completion -*- shell-script -*- + """ + And the output should contain: + """ + complete -F _linear-cli_completions lc """ diff --git a/features/issue_list.feature b/features/issue_list.feature new file mode 100644 index 0000000..68fa483 --- /dev/null +++ b/features/issue_list.feature @@ -0,0 +1,17 @@ +Feature: Issue List + As a user + I want to list issues + So that I can see what I need to work on + + Scenario: Listing my issues + When I run `lc issue list` + Then the output should match /[\w-]+/ + + Scenario: Listing all issues + When I run `lc issue list --no-mine` + Then the output should match /[\w-]+/ + + Scenario: Listing specific issue + When I run `lc issue list NOCRY-123` + Then the exit status should be 66 + And the output should match /Record not found/ diff --git a/features/team_list.feature b/features/team_list.feature new file mode 100644 index 0000000..5e3b5c5 --- /dev/null +++ b/features/team_list.feature @@ -0,0 +1,12 @@ +Feature: Team List + As a user + I want to list teams + So that I can see what teams are available + + Scenario: Listing my teams + When I run `lc team list` + Then the output should match /[\w-]+/ + + Scenario: Listing all teams + When I run `lc team list --no-mine` + Then the output should match /[\w-]+/ diff --git a/features/whoami.feature b/features/whoami.feature new file mode 100644 index 0000000..6a13b78 --- /dev/null +++ b/features/whoami.feature @@ -0,0 +1,8 @@ +Feature: WhoAmI + As a user + I want to know who I am logged in as + So that I can verify my credentials + + Scenario: Displaying the current user + When I run `lc whoami` + Then the output should match /[\w-]+: .+ <.+>/ diff --git a/lib/linear.rb b/lib/linear.rb index bf4fe2f..4a5bf64 100644 --- a/lib/linear.rb +++ b/lib/linear.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true require 'pathname' +require 'trailblazer' +require 'trailblazer/operation' require 'semantic_logger' SemanticLogger.default_level = :info SemanticLogger.add_appender(io: $stderr, formatter: :color) @@ -21,6 +23,7 @@ module Linear ROOT = (Pathname(__FILE__)/'../..').expand_path LIBROOT = ROOT/:lib/:linear MODEL_ROOT = ROOT/:lib/:linear/:models + OPERATION_ROOT = ROOT/:lib/:linear/:operations SPEC_ROOT = ROOT/:spec FEATURE_ROOT = ROOT/:features DEBUG_LEVELS = %i[warn info debug trace].freeze @@ -41,6 +44,10 @@ def self.L(*libraries) # rubocop:disable Naming/MethodName def self.M(*models) # rubocop:disable Naming/MethodName Array(models).each { |model| require MODEL_ROOT/model } end + + def self.O(*operations) # rubocop:disable Naming/MethodName + Array(operations).each { |operation| require OPERATION_ROOT/operation } + end # rubocop:enable Layout/SpaceAroundOperators def self.verbosity diff --git a/lib/linear/cli.rb b/lib/linear/cli.rb index aea30ae..0c6a56a 100644 --- a/lib/linear/cli.rb +++ b/lib/linear/cli.rb @@ -2,7 +2,6 @@ require 'dry/cli' require 'dry/cli/completion/command' -require_relative '../linear' require 'semantic_logger' require 'tty-markdown' require 'tty-prompt' diff --git a/lib/linear/cli/what_for.rb b/lib/linear/cli/what_for.rb index eb17be1..85a23b6 100644 --- a/lib/linear/cli/what_for.rb +++ b/lib/linear/cli/what_for.rb @@ -93,7 +93,7 @@ def pr_title_for(issue) proposed = [pr_type_for(issue)] proposed_scope = pr_scope_for(issue.title) proposed << "(#{proposed_scope})" if proposed_scope - summary = issue.title.sub(/(?:#{ALLOWED_PR_TYPES})(\([^)]+\))? /, '') + summary = issue.title.sub(/(?:#{ALLOWED_PR_TYPES})(\([^)]+\))? /o, '') proposed << ": #{issue.identifier} - #{summary}" prompt.ask("Title for PR for #{issue.identifier} - #{summary}", default: proposed.join) end @@ -113,7 +113,7 @@ def pr_description_for(issue) end def pr_type_for(issue) - proposed_type = issue.title.match(/^(#{ALLOWED_PR_TYPES})/i) + proposed_type = issue.title.match(/^(#{ALLOWED_PR_TYPES})/io) return proposed_type[1].downcase if proposed_type prompt.select('What type of PR is this?', PR_TYPE_SELECTIONS) diff --git a/lib/linear/commands/issue/create.rb b/lib/linear/commands/issue/create.rb index fa86904..b6ffb86 100644 --- a/lib/linear/commands/issue/create.rb +++ b/lib/linear/commands/issue/create.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'semantic_logger' -require_relative '../issue' module Rubyists # Namespace for Linear diff --git a/lib/linear/commands/issue/develop.rb b/lib/linear/commands/issue/develop.rb index c1b6604..6631f50 100644 --- a/lib/linear/commands/issue/develop.rb +++ b/lib/linear/commands/issue/develop.rb @@ -2,7 +2,6 @@ require 'semantic_logger' require 'git' -require_relative '../issue' module Rubyists # Namespace for Linear diff --git a/lib/linear/commands/issue/list.rb b/lib/linear/commands/issue/list.rb index 14dd186..fe7be4f 100644 --- a/lib/linear/commands/issue/list.rb +++ b/lib/linear/commands/issue/list.rb @@ -7,6 +7,7 @@ module Rubyists module Linear M :issue M :user + O 'issue/list' # Namespace for CLI module CLI module Issue @@ -37,31 +38,13 @@ class List def call(ids:, **options) logger.debug 'Listing issues' - return display(issues_for(options), options) if ids.empty? - - display issues_for(options.merge(ids:)), options - end - - def filters_for(options) - filter = {} - filter[:assignee] = { isMe: { eq: true } } if options[:mine] - filter[:assignee] = { null: true } if options[:unassigned] - filter[:team] = { key: { eq: options[:team] } } if options[:team] - - if options[:project] - project = project_for(options[:project]) - logger.debug('Found project', project:) - filter[:project] = { id: { eq: project.id } } if project + project = project_for(options[:project]) if options[:project] + result = Rubyists::Linear::Operations::Issue::List.call(params: options.merge(ids:), project: project) + if result.success? + display result[:issues], options + else + logger.error 'Failed to list issues' end - - filter - end - - def issues_for(options) - logger.debug('Fetching issues', options:) - return options[:ids].map { |id| Rubyists::Linear::Issue.find(id.upcase) } if options[:ids] - - Rubyists::Linear::Issue.all filter: filters_for(options) end end end diff --git a/lib/linear/commands/issue/pr.rb b/lib/linear/commands/issue/pr.rb index e20bdb6..755ed80 100644 --- a/lib/linear/commands/issue/pr.rb +++ b/lib/linear/commands/issue/pr.rb @@ -2,7 +2,6 @@ require 'semantic_logger' require 'git' -require_relative '../issue' module Rubyists # Namespace for Linear diff --git a/lib/linear/commands/issue/take.rb b/lib/linear/commands/issue/take.rb index 8bd9794..9ccf427 100644 --- a/lib/linear/commands/issue/take.rb +++ b/lib/linear/commands/issue/take.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'semantic_logger' -require_relative '../issue' module Rubyists # Namespace for Linear @@ -21,13 +20,13 @@ class Take argument :issue_ids, type: :array, required: true, desc: 'Issue Identifiers' def call(issue_ids:, **options) - updates = issue_ids.map do |issue_id| + updates = issue_ids.filter_map do |issue_id| Rubyists::Linear::Issue.find(issue_id) gimme_da_issue! issue_id # gimme_da_issue! is defined in Rubyists::Linear::CLI::Issue rescue NotFoundError => e logger.warn e.message next - end.compact + end display updates, options end end diff --git a/lib/linear/commands/issue/update.rb b/lib/linear/commands/issue/update.rb index bf285b5..590caf0 100644 --- a/lib/linear/commands/issue/update.rb +++ b/lib/linear/commands/issue/update.rb @@ -2,7 +2,6 @@ require 'semantic_logger' require 'git' -require_relative '../issue' module Rubyists # Namespace for Linear diff --git a/lib/linear/commands/team/list.rb b/lib/linear/commands/team/list.rb index 4b4651e..7662566 100644 --- a/lib/linear/commands/team/list.rb +++ b/lib/linear/commands/team/list.rb @@ -6,6 +6,7 @@ module Rubyists # Namespace for Linear module Linear M :team, :issue + O 'team/list' # Namespace for CLI module CLI module Team @@ -19,13 +20,12 @@ class List def call(**options) logger.debug 'Listing teams' - display teams_for(options), options - end - - def teams_for(options) - return Rubyists::Linear::Team.mine if options[:mine] - - Rubyists::Linear::Team.all + result = Rubyists::Linear::Operations::Team::List.call(params: options) + if result.success? + display result[:teams], options + else + logger.error 'Failed to list teams' + end end prepend Rubyists::Linear::CLI::Caller diff --git a/lib/linear/commands/whoami.rb b/lib/linear/commands/whoami.rb index 5032b04..fa2b43b 100644 --- a/lib/linear/commands/whoami.rb +++ b/lib/linear/commands/whoami.rb @@ -6,6 +6,7 @@ module Rubyists # Namespace for Linear module Linear M :user + O :whoami # Namespace for CLI module CLI WhoAmI = Class.new Dry::CLI::Command @@ -20,7 +21,12 @@ class WhoAmI def call(**options) logger.debug 'Getting user info' - display Rubyists::Linear::User.me(teams: options[:teams]), options + result = Rubyists::Linear::Operations::WhoAmI.call(params: options) + if result.success? + display result[:user], options + else + logger.error 'Failed to get user info' + end end prepend Rubyists::Linear::CLI::Caller diff --git a/lib/linear/models/base_model/method_magic.rb b/lib/linear/models/base_model/method_magic.rb index 59960a0..040740a 100644 --- a/lib/linear/models/base_model/method_magic.rb +++ b/lib/linear/models/base_model/method_magic.rb @@ -5,13 +5,13 @@ module Linear class BaseModel # Methods for Linear models. module MethodMagic - def self.included(base) # rubocop:disable Metrics/AbcSize + def self.included(base) base.instance_eval do base.base_fragment.__nodes.each do |node| sym = node.__name.to_sym - define_method(sym) { updated_data[sym] } unless instance_methods.include? sym + define_method(sym) { updated_data[sym] } unless method_defined?(sym) esym = :"#{sym}=" - next if instance_methods.include? esym + next if method_defined?(esym) define_method(esym) { |value| updated_data[sym] = value } end diff --git a/lib/linear/models/issue.rb b/lib/linear/models/issue.rb index bea8677..37fc6c4 100644 --- a/lib/linear/models/issue.rb +++ b/lib/linear/models/issue.rb @@ -9,7 +9,7 @@ module Linear Issue = Class.new(BaseModel) M 'issue/class_methods' # The Issue class represents a Linear issue. - class Issue # rubocop:disable Metrics/ClassLength + class Issue include SemanticLogger::Loggable extend ClassMethods diff --git a/lib/linear/models/team.rb b/lib/linear/models/team.rb index 0ebb4f5..f68b6a7 100644 --- a/lib/linear/models/team.rb +++ b/lib/linear/models/team.rb @@ -5,7 +5,7 @@ module Rubyists # Namespace for Linear module Linear - M :base_model, :issue, :project, :workflow_state, :user + M :base_model Team = Class.new(BaseModel) # The Issue class represents a Linear issue. class Team @@ -65,15 +65,15 @@ def label_groups @label_groups ||= [] end - def labels # rubocop:disable Metrics/CyclomaticComplexity + def labels return @labels if @labels - @labels = Api.query(label_query).dig(:team, :labels, :nodes)&.map do |label| + @labels = Api.query(label_query).dig(:team, :labels, :nodes)&.filter_map do |label| label_groups << Label.new(label) if label[:isGroup] next if label[:isGroup] || label[:parent] Label.new label - end&.compact + end end def members diff --git a/lib/linear/models/user.rb b/lib/linear/models/user.rb index 3b98cb3..1b0c29e 100644 --- a/lib/linear/models/user.rb +++ b/lib/linear/models/user.rb @@ -6,7 +6,7 @@ module Rubyists # Namespace for Linear module Linear L :api - M :base_model, :issue, :team + M :base_model User = Class.new(BaseModel) # The User class represents a Linear user. class User diff --git a/lib/linear/operations/issue/list.rb b/lib/linear/operations/issue/list.rb new file mode 100644 index 0000000..a48f9f5 --- /dev/null +++ b/lib/linear/operations/issue/list.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Rubyists + module Linear + module Operations + module Issue + # The List operation lists issues + class List < Trailblazer::Operation + step :build_filter + step :fetch_issues + + def build_filter(ctx, params:, project: nil, **) + filter = {} + filter[:assignee] = { isMe: { eq: true } } if params[:mine] + filter[:assignee] = { null: true } if params[:unassigned] + filter[:team] = { key: { eq: params[:team] } } if params[:team] + filter[:project] = { id: { eq: project.id } } if project + ctx[:filter] = filter + end + + def fetch_issues(ctx, params:, filter:, **) + ctx[:issues] = if params[:ids] && !params[:ids].empty? + params[:ids].map { |id| Rubyists::Linear::Issue.find(id.upcase) } + else + Rubyists::Linear::Issue.all(filter:) + end + end + end + end + end + end +end diff --git a/lib/linear/operations/team/list.rb b/lib/linear/operations/team/list.rb new file mode 100644 index 0000000..7ba8f05 --- /dev/null +++ b/lib/linear/operations/team/list.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Rubyists + module Linear + module Operations + module Team + # The List operation lists teams + class List < Trailblazer::Operation + step :fetch_teams + + def fetch_teams(ctx, params:, **) + ctx[:teams] = if params[:mine] + Rubyists::Linear::Team.mine + else + Rubyists::Linear::Team.all + end + end + end + end + end + end +end diff --git a/lib/linear/operations/whoami.rb b/lib/linear/operations/whoami.rb new file mode 100644 index 0000000..fe2c8eb --- /dev/null +++ b/lib/linear/operations/whoami.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Rubyists + module Linear + module Operations + # The WhoAmI operation returns the current user + class WhoAmI < Trailblazer::Operation + step :fetch_user + + def fetch_user(ctx, params:, **) + ctx[:user] = Rubyists::Linear::User.me(teams: params[:teams]) + end + end + end + end +end diff --git a/linear-cli.gemspec b/linear-cli.gemspec index dcccac0..52c5143 100644 --- a/linear-cli.gemspec +++ b/linear-cli.gemspec @@ -45,6 +45,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'semantic_logger' spec.add_dependency 'sequel' spec.add_dependency 'sqlite3' + spec.add_dependency 'trailblazer' spec.add_dependency 'tty-editor' spec.add_dependency 'tty-markdown' spec.add_dependency 'tty-prompt' diff --git a/spec/helper.rb b/spec/helper.rb deleted file mode 100644 index abac065..0000000 --- a/spec/helper.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -require_relative '../lib/linear' diff --git a/spec/linear/cli_spec.rb b/spec/linear/cli_spec.rb deleted file mode 100644 index 2dcfacf..0000000 --- a/spec/linear/cli_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -require_relative '../helper' - -RSpec.describe 'Rubyists::Linear::CLI' do - it 'has a version number' do - expect(Rubyists::Linear::VERSION).not_to be_nil - end -end diff --git a/spec/linear/models/project_spec.rb b/spec/linear/models/project_spec.rb deleted file mode 100644 index 908c9bc..0000000 --- a/spec/linear/models/project_spec.rb +++ /dev/null @@ -1,54 +0,0 @@ -# frozen_string_literal: true - -require 'helper' - -RSpec.describe 'Rubyists::Linear::Project' do - let(:instance) { Rubyists::Linear::Project.new(attributes) } - let(:attributes) do - { - id: SecureRandom.alphanumeric(32), - name: "Project #{Time.now.to_i}", - content: "Content at #{Time.now}", - slugId: SecureRandom.alphanumeric(8), - description: "Gadzooks! It's #{Time.now}!", - url: 'https://linear.app/myorg/project/my-project-a2d0ddbbfbeb', - createdAt: Time.now - 86_400, - updatedAt: Time.now - 3600 - } - end - - describe '#match_score?' do - context 'when arg equals matching url' do - subject { instance.match_score?(attributes.fetch(:url)) } - - it { is_expected.to eq(100) } - end - - context 'when arg equals project ID' do - subject { instance.match_score?(attributes.fetch(:id)) } - - it { is_expected.to eq(100) } - end - - context 'when arg equals project name' do - subject { instance.match_score?(attributes.fetch(:name)) } - - it { is_expected.to eq(100) } - end - - context 'when arg is part of project name' do - # Argument is project name with last 5 characters removed - subject { instance.match_score?(attributes.fetch(:name)[0..-5]) } - - it { is_expected.to eq(75) } - end - - context 'when description includes arg' do - subject { instance.match_score?(project_name) } - - let(:project_name) { 'gadzooks' } - - it { is_expected.to eq(50) } - end - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb deleted file mode 100644 index 81b534d..0000000 --- a/spec/spec_helper.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -require 'linear/cli' - -RSpec.configure do |config| - # Enable flags like --only-failures and --next-failure - config.example_status_persistence_file_path = '.rspec_status' - - # Disable RSpec exposing methods globally on `Module` and `main` - config.disable_monkey_patching! - - config.expect_with :rspec do |c| - c.syntax = :expect - end -end diff --git a/test/linear/cli_test.rb b/test/linear/cli_test.rb new file mode 100644 index 0000000..24bfbf4 --- /dev/null +++ b/test/linear/cli_test.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require_relative '../test_helper' +require 'linear/cli' + +class CLITest < Minitest::Spec + describe 'Rubyists::Linear::CLI' do + it 'has a version number' do + _(Rubyists::Linear::VERSION).wont_be_nil + end + end +end diff --git a/test/linear/models/project_test.rb b/test/linear/models/project_test.rb new file mode 100644 index 0000000..ff2d0e3 --- /dev/null +++ b/test/linear/models/project_test.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require_relative '../../test_helper' +require 'linear/models/project' + +class ProjectTest < Minitest::Spec + describe 'Rubyists::Linear::Project' do + let(:attributes) do + { + id: SecureRandom.alphanumeric(32), + name: "Project #{Time.now.to_i}", + content: "Content at #{Time.now}", + slugId: SecureRandom.alphanumeric(8), + description: "Gadzooks! It's #{Time.now}!", + url: 'https://linear.app/myorg/project/my-project-a2d0ddbbfbeb', + createdAt: Time.now - 86_400, + updatedAt: Time.now - 3600 + } + end + let(:instance) { Rubyists::Linear::Project.new(attributes) } + + describe '#match_score?' do + context 'when arg equals matching url' do + it 'returns 100' do + _(instance.match_score?(attributes.fetch(:url))).must_equal 100 + end + end + + context 'when arg equals project ID' do + it 'returns 100' do + _(instance.match_score?(attributes.fetch(:id))).must_equal 100 + end + end + + context 'when arg equals project name' do + it 'returns 100' do + _(instance.match_score?(attributes.fetch(:name))).must_equal 100 + end + end + + context 'when arg is part of project name' do + it 'returns 75' do + # Argument is project name with last 5 characters removed + _(instance.match_score?(attributes.fetch(:name)[0..-5])).must_equal 75 + end + end + + context 'when description includes arg' do + let(:project_name) { 'gadzooks' } + + it 'returns 50' do + _(instance.match_score?(project_name)).must_equal 50 + end + end + end + end +end diff --git a/test/linear/operations/issue/list_test.rb b/test/linear/operations/issue/list_test.rb new file mode 100644 index 0000000..f418013 --- /dev/null +++ b/test/linear/operations/issue/list_test.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require_relative '../../../test_helper' +require 'linear/operations/issue/list' + +class IssueListOperationTest < Minitest::Spec + describe 'Rubyists::Linear::Operations::Issue::List' do + let(:issues) { [Object.new, Object.new] } + let(:project) { Struct.new(:id).new('project-id') } + + describe 'when ids are provided' do + let(:params) { { ids: %w[ISS-1 ISS-2] } } + + it 'fetches specific issues' do + Rubyists::Linear::Issue.stub :find, issues.first do + result = Rubyists::Linear::Operations::Issue::List.call(params: params) + + _(result.success?).must_equal true + _(result[:issues].size).must_equal 2 + end + end + end + + describe 'when no ids are provided' do + let(:params) { { mine: true } } + + it 'fetches issues with filter' do + Rubyists::Linear::Issue.stub :all, issues do + result = Rubyists::Linear::Operations::Issue::List.call(params: params) + + _(result.success?).must_equal true + _(result[:issues]).must_equal issues + _(result[:filter]).must_equal({ assignee: { isMe: { eq: true } } }) + end + end + end + + describe 'when project is provided' do + let(:params) { { project: 'some-project' } } + + it 'fetches issues with project filter' do + Rubyists::Linear::Issue.stub :all, issues do + result = Rubyists::Linear::Operations::Issue::List.call(params: params, project: project) + + _(result.success?).must_equal true + _(result[:issues]).must_equal issues + _(result[:filter]).must_equal({ project: { id: { eq: 'project-id' } } }) + end + end + end + end +end diff --git a/test/linear/operations/team/list_test.rb b/test/linear/operations/team/list_test.rb new file mode 100644 index 0000000..f66c663 --- /dev/null +++ b/test/linear/operations/team/list_test.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require_relative '../../../test_helper' +require 'linear/operations/team/list' + +class TeamListOperationTest < Minitest::Spec + describe 'Rubyists::Linear::Operations::Team::List' do + let(:teams) { [Object.new, Object.new] } + + describe 'when mine is true' do + let(:params) { { mine: true } } + + it 'fetches my teams' do + Rubyists::Linear::Team.stub :mine, teams do + result = Rubyists::Linear::Operations::Team::List.call(params: params) + + _(result.success?).must_equal true + _(result[:teams]).must_equal teams + end + end + end + + describe 'when mine is false' do + let(:params) { { mine: false } } + + it 'fetches all teams' do + Rubyists::Linear::Team.stub :all, teams do + result = Rubyists::Linear::Operations::Team::List.call(params: params) + + _(result.success?).must_equal true + _(result[:teams]).must_equal teams + end + end + end + end +end diff --git a/test/linear/operations/whoami_test.rb b/test/linear/operations/whoami_test.rb new file mode 100644 index 0000000..4d36bec --- /dev/null +++ b/test/linear/operations/whoami_test.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require_relative '../../test_helper' +require 'linear/operations/whoami' + +class WhoAmIOperationTest < Minitest::Spec + describe 'Rubyists::Linear::Operations::WhoAmI' do + let(:user) { Object.new } + let(:params) { { teams: false } } + + it 'fetches the user' do + Rubyists::Linear::User.stub :me, user do + result = Rubyists::Linear::Operations::WhoAmI.call(params: params) + + _(result.success?).must_equal true + _(result[:user]).must_equal user + end + end + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..9c73c7e --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +$LOAD_PATH.unshift File.expand_path('../lib', __dir__) +require 'linear' +require 'minitest/autorun' +require 'minitest/spec' +require 'shoulda/context'