From 2cf17d47615eabb0fc21d227e02683ef90fb2471 Mon Sep 17 00:00:00 2001 From: "Daniel (dB.) Doubrovkine" Date: Sat, 15 Jun 2024 09:21:20 -0400 Subject: [PATCH] Upgraded dependencies. --- .rubocop.yml | 4 + .rubocop_todo.yml | 200 +++++++++++++- Gemfile | 5 +- Gemfile.lock | 244 ++++++++++-------- config/initializers/slack_ruby_bot_client.rb | 2 +- slack-gamebot/api/helpers/sort_helpers.rb | 2 +- .../api/presenters/user_presenter.rb | 2 +- slack-gamebot/server.rb | 2 +- spec/api/cors_spec.rb | 2 + .../api/endpoints/challenges_endpoint_spec.rb | 4 + .../endpoints/credit_cards_endpoint_spec.rb | 7 +- spec/api/endpoints/games_endpoint_spec.rb | 2 + spec/api/endpoints/matches_endpoint_spec.rb | 3 + spec/api/endpoints/root_endpoint_spec.rb | 4 +- spec/api/endpoints/seasons_endpoint_spec.rb | 4 + spec/api/endpoints/status_endpoint_spec.rb | 4 + .../endpoints/subscriptions_endpoint_spec.rb | 27 +- spec/api/endpoints/teams_endpoint_spec.rb | 52 ++-- spec/api/endpoints/users_endpoint_spec.rb | 8 + spec/api/swagger_documentation_spec.rb | 2 + spec/fabricators/season_fabricator.rb | 2 +- spec/initializers/array_spec.rb | 15 +- spec/initializers/giphy_spec.rb | 4 +- spec/integration/index_spec.rb | 4 +- spec/integration/privacy_spec.rb | 3 +- spec/integration/subscribe_spec.rb | 53 ++-- spec/integration/teams_spec.rb | 5 +- spec/integration/update_cc_spec.rb | 12 +- spec/models/challenge_spec.rb | 81 +++++- spec/models/elo_spec.rb | 2 +- spec/models/game_spec.rb | 24 +- spec/models/match_spec.rb | 50 +++- spec/models/score_spec.rb | 27 +- spec/models/season_spec.rb | 30 ++- spec/models/team_spec.rb | 85 ++++-- spec/models/user_rank_spec.rb | 2 +- spec/models/user_spec.rb | 80 ++++-- spec/slack-gamebot/app_spec.rb | 35 ++- spec/slack-gamebot/commands/accept_spec.rb | 5 + spec/slack-gamebot/commands/cancel_spec.rb | 4 + .../commands/challenge_question_spec.rb | 12 +- spec/slack-gamebot/commands/challenge_spec.rb | 19 +- .../slack-gamebot/commands/challenges_spec.rb | 3 + spec/slack-gamebot/commands/decline_spec.rb | 1 + spec/slack-gamebot/commands/default_spec.rb | 2 + spec/slack-gamebot/commands/demote_spec.rb | 6 + spec/slack-gamebot/commands/draw_spec.rb | 26 +- spec/slack-gamebot/commands/help_spec.rb | 4 + spec/slack-gamebot/commands/hi_spec.rb | 1 + spec/slack-gamebot/commands/info_spec.rb | 1 + .../commands/leaderboard_spec.rb | 11 + spec/slack-gamebot/commands/lost_spec.rb | 38 ++- spec/slack-gamebot/commands/matches_spec.rb | 16 ++ spec/slack-gamebot/commands/promote_spec.rb | 6 + spec/slack-gamebot/commands/rank_spec.rb | 9 + spec/slack-gamebot/commands/register_spec.rb | 11 +- spec/slack-gamebot/commands/reset_spec.rb | 25 +- spec/slack-gamebot/commands/resigned_spec.rb | 11 +- spec/slack-gamebot/commands/season_spec.rb | 9 +- spec/slack-gamebot/commands/seasons_spec.rb | 13 + spec/slack-gamebot/commands/set_spec.rb | 85 +++++- .../commands/subscription_spec.rb | 14 +- spec/slack-gamebot/commands/sucks_spec.rb | 7 + spec/slack-gamebot/commands/taunt_spec.rb | 5 + spec/slack-gamebot/commands/team_spec.rb | 5 + spec/slack-gamebot/commands/unknown_spec.rb | 4 +- .../slack-gamebot/commands/unregister_spec.rb | 9 +- .../commands/unsubscribe_spec.rb | 21 +- spec/slack-gamebot/server_spec.rb | 16 +- spec/slack-gamebot/service_spec.rb | 15 +- spec/slack-gamebot/version_spec.rb | 2 +- .../endpoints/it_behaves_like_a_cursor_api.rb | 3 +- spec/support/database_cleaner.rb | 2 +- spec/support/stripe.rb | 3 +- 74 files changed, 1200 insertions(+), 318 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index aa6ce20f..66f1a57a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -19,3 +19,7 @@ Style/FrozenStringLiteralComment: Enabled: false inherit_from: .rubocop_todo.yml +require: + - rubocop-rake + - rubocop-rspec + - rubocop-capybara \ No newline at end of file diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 1e3a0581..6343de29 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,13 +1,28 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2024-02-11 17:20:17 UTC using RuboCop version 1.30.1. +# on 2024-06-15 12:58:07 UTC using RuboCop version 1.64.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. +# Offense count: 3 +# Configuration parameters: EnforcedStyle. +# SupportedStyles: link_or_button, strict +Capybara/ClickLinkOrButtonStyle: + Exclude: + - 'spec/integration/index_spec.rb' + - 'spec/integration/update_cc_spec.rb' + +# Offense count: 4 +Capybara/SpecificActions: + Exclude: + - 'spec/integration/subscribe_spec.rb' + - 'spec/integration/update_cc_spec.rb' + # Offense count: 4 -# Configuration parameters: IgnoredMethods. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowedMethods, AllowedPatterns. Lint/AmbiguousBlockAssociation: Exclude: - 'spec/api/endpoints/challenges_endpoint_spec.rb' @@ -19,11 +34,12 @@ Lint/AmbiguousBlockAssociation: # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS Naming/FileName: Exclude: + - 'Rakefile.rb' - 'slack-gamebot.rb' # Offense count: 6 # Configuration parameters: ForbiddenDelimiters. -# ForbiddenDelimiters: (?-mix:(^|\s)(EO[A-Z]{1}|END)(\s|$)) +# ForbiddenDelimiters: (?i-mx:(^|\s)(EO[A-Z]{1}|END)(\s|$)) Naming/HeredocDelimiterNaming: Exclude: - 'slack-gamebot/app.rb' @@ -33,6 +49,7 @@ Naming/HeredocDelimiterNaming: - 'slack-gamebot/server.rb' # Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyleForLeadingUnderscores. # SupportedStylesForLeadingUnderscores: disallowed, required, optional Naming/MemoizedInstanceVariableName: @@ -41,7 +58,7 @@ Naming/MemoizedInstanceVariableName: # Offense count: 17 # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. -# AllowedNames: at, by, db, id, in, io, ip, of, on, os, pp, to +# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to Naming/MethodParameterName: Exclude: - 'slack-gamebot/app.rb' @@ -51,19 +68,173 @@ Naming/MethodParameterName: # Offense count: 16 # Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns. # SupportedStyles: snake_case, normalcase, non_integer -# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339 +# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64 Naming/VariableNumber: Exclude: - 'spec/slack-gamebot/commands/leaderboard_spec.rb' - 'spec/slack-gamebot/commands/rank_spec.rb' -# Offense count: 3 +# Offense count: 39 +RSpec/AnyInstance: + Exclude: + - 'spec/api/endpoints/credit_cards_endpoint_spec.rb' + - 'spec/api/endpoints/status_endpoint_spec.rb' + - 'spec/api/endpoints/subscriptions_endpoint_spec.rb' + - 'spec/api/endpoints/teams_endpoint_spec.rb' + - 'spec/integration/subscribe_spec.rb' + - 'spec/integration/teams_spec.rb' + - 'spec/models/team_spec.rb' + - 'spec/slack-gamebot/app_spec.rb' + - 'spec/slack-gamebot/commands/accept_spec.rb' + - 'spec/slack-gamebot/commands/draw_spec.rb' + - 'spec/slack-gamebot/commands/matches_spec.rb' + - 'spec/slack-gamebot/commands/sucks_spec.rb' + +# Offense count: 130 +# Configuration parameters: Prefixes, AllowedPatterns. +# Prefixes: when, with, without +RSpec/ContextWording: + Enabled: false + +# Offense count: 99 # This cop supports unsafe autocorrection (--autocorrect-all). -Style/CaseLikeIf: +# Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. +# SupportedStyles: described_class, explicit +RSpec/DescribedClass: + Exclude: + - 'spec/initializers/giphy_spec.rb' + - 'spec/models/challenge_spec.rb' + - 'spec/models/elo_spec.rb' + - 'spec/models/game_spec.rb' + - 'spec/models/match_spec.rb' + - 'spec/models/score_spec.rb' + - 'spec/models/season_spec.rb' + - 'spec/models/team_spec.rb' + - 'spec/models/user_rank_spec.rb' + - 'spec/models/user_spec.rb' + - 'spec/slack-gamebot/app_spec.rb' + - 'spec/slack-gamebot/server_spec.rb' + - 'spec/slack-gamebot/service_spec.rb' + +# Offense count: 93 +# Configuration parameters: CountAsOne. +RSpec/ExampleLength: + Max: 31 + +# Offense count: 4 +RSpec/ExpectInHook: + Exclude: + - 'spec/api/endpoints/credit_cards_endpoint_spec.rb' + - 'spec/api/endpoints/status_endpoint_spec.rb' + - 'spec/api/endpoints/subscriptions_endpoint_spec.rb' + +# Offense count: 26 +# Configuration parameters: Max, AllowedIdentifiers, AllowedPatterns. +RSpec/IndexedLet: + Exclude: + - 'spec/api/endpoints/teams_endpoint_spec.rb' + - 'spec/api/endpoints/users_endpoint_spec.rb' + - 'spec/slack-gamebot/commands/leaderboard_spec.rb' + - 'spec/slack-gamebot/commands/lost_spec.rb' + - 'spec/slack-gamebot/commands/matches_spec.rb' + - 'spec/slack-gamebot/commands/rank_spec.rb' + +# Offense count: 2 +# Configuration parameters: AssignmentOnly. +RSpec/InstanceVariable: + Exclude: + - 'spec/slack-gamebot/service_spec.rb' + - 'spec/support/api/endpoints/it_behaves_like_a_cursor_api.rb' + +# Offense count: 48 +RSpec/LetSetup: + Enabled: false + +# Offense count: 1 +RSpec/MessageChain: Exclude: - - 'slack-gamebot/commands/draw.rb' - - 'slack-gamebot/commands/lost.rb' - - 'slack-gamebot/commands/resigned.rb' + - 'spec/slack-gamebot/server_spec.rb' + +# Offense count: 56 +# Configuration parameters: . +# SupportedStyles: have_received, receive +RSpec/MessageSpies: + EnforcedStyle: receive + +# Offense count: 245 +RSpec/MultipleExpectations: + Max: 18 + +# Offense count: 23 +# Configuration parameters: AllowSubject. +RSpec/MultipleMemoizedHelpers: + Max: 10 + +# Offense count: 10 +# Configuration parameters: EnforcedStyle, IgnoreSharedExamples. +# SupportedStyles: always, named_only +RSpec/NamedSubject: + Exclude: + - 'spec/api/swagger_documentation_spec.rb' + - 'spec/slack-gamebot/app_spec.rb' + +# Offense count: 21 +# Configuration parameters: AllowedGroups. +RSpec/NestedGroups: + Max: 4 + +# Offense count: 1 +RSpec/OverwritingSetup: + Exclude: + - 'spec/models/season_spec.rb' + +# Offense count: 12 +RSpec/RepeatedDescription: + Exclude: + - 'spec/slack-gamebot/commands/set_spec.rb' + +# Offense count: 12 +RSpec/RepeatedExample: + Exclude: + - 'spec/slack-gamebot/commands/set_spec.rb' + +# Offense count: 2 +RSpec/RepeatedExampleGroupBody: + Exclude: + - 'spec/api/endpoints/teams_endpoint_spec.rb' + +# Offense count: 10 +RSpec/RepeatedExampleGroupDescription: + Exclude: + - 'spec/api/endpoints/games_endpoint_spec.rb' + - 'spec/api/endpoints/matches_endpoint_spec.rb' + - 'spec/api/endpoints/teams_endpoint_spec.rb' + - 'spec/slack-gamebot/commands/set_spec.rb' + +# Offense count: 38 +# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. +# Include: **/*_spec.rb +RSpec/SpecFilePathFormat: + Enabled: false + +# Offense count: 11 +RSpec/StubbedMock: + Exclude: + - 'spec/api/endpoints/status_endpoint_spec.rb' + - 'spec/api/endpoints/subscriptions_endpoint_spec.rb' + - 'spec/api/endpoints/teams_endpoint_spec.rb' + - 'spec/integration/subscribe_spec.rb' + - 'spec/integration/update_cc_spec.rb' + - 'spec/slack-gamebot/app_spec.rb' + +# Offense count: 11 +# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. +RSpec/VerifiedDoubles: + Exclude: + - 'spec/api/endpoints/subscriptions_endpoint_spec.rb' + - 'spec/api/endpoints/teams_endpoint_spec.rb' + - 'spec/integration/subscribe_spec.rb' + - 'spec/models/user_spec.rb' # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -75,6 +246,7 @@ Style/GlobalStdStream: # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedReceivers. +# AllowedReceivers: Thread.current Style/HashEachMethods: Exclude: - 'slack-gamebot/models/challenge.rb' @@ -91,6 +263,12 @@ Style/HashTransformValues: Exclude: - 'slack-gamebot/api/helpers/error_helpers.rb' +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/MapIntoArray: + Exclude: + - 'slack-gamebot/models/score.rb' + # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). Style/MapToHash: @@ -105,7 +283,7 @@ Style/MultilineBlockChain: # Offense count: 6 # This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle, IgnoredMethods. +# Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns. # SupportedStyles: predicate, comparison Style/NumericPredicate: Exclude: diff --git a/Gemfile b/Gemfile index 24964b63..9e7cbfa4 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,7 @@ source 'http://rubygems.org' ruby '3.0.5' +gem 'faraday_middleware' gem 'hashie', '3.4.6' gem 'irb' gem 'json' @@ -12,6 +13,7 @@ gem 'mongoid' gem 'mongoid-scroll' gem 'multi_json' gem 'newrelic_rpm' +gem 'rack', '~> 2' gem 'rack-robotz' gem 'rack-server-pages' gem 'roar' @@ -28,6 +30,7 @@ group :development, :test do gem 'foreman' gem 'rake', '~> 12.3' gem 'rubocop' + gem 'rubocop-capybara' gem 'rubocop-faker' gem 'rubocop-rake' gem 'rubocop-rspec' @@ -44,7 +47,7 @@ group :test do gem 'excon' gem 'fabrication' gem 'faker' - gem 'hyperclient' + gem 'hyperclient', '~> 1.0.0' gem 'rack-test' gem 'rspec' gem 'selenium-webdriver' diff --git a/Gemfile.lock b/Gemfile.lock index 36835bb0..5f5897b0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,41 +1,45 @@ GEM remote: http://rubygems.org/ specs: - activemodel (7.0.7.2) - activesupport (= 7.0.7.2) - activesupport (7.0.7.2) + activemodel (7.0.8.4) + activesupport (= 7.0.8.4) + activesupport (7.0.8.4) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) ast (2.4.2) - async (1.30.2) + async (1.32.1) console (~> 1.10) nio4r (~> 2.3) timers (~> 4.1) - async-io (1.33.0) + async-io (1.43.2) async async-websocket (0.8.0) async-io websocket-driver (~> 0.7.0) + base64 (0.2.0) + bigdecimal (3.1.8) bson (4.15.0) - builder (3.2.4) - capybara (3.36.0) + builder (3.3.0) + capybara (3.40.0) addressable matrix mini_mime (>= 0.1.3) - nokogiri (~> 1.8) + nokogiri (~> 1.11) rack (>= 1.6.0) rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - childprocess (4.1.0) - concurrent-ruby (1.2.2) - console (1.15.3) + concurrent-ruby (1.3.3) + console (1.24.0) + fiber-annotation fiber-local - crack (0.4.5) + json + crack (1.0.0) + bigdecimal rexml dante (0.2.0) database_cleaner-core (2.0.1) @@ -43,32 +47,28 @@ GEM database_cleaner-core (~> 2.0.0) mongoid declarative (0.0.20) - diff-lcs (1.5.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) - dry-configurable (0.13.0) - concurrent-ruby (~> 1.0) - dry-core (~> 0.6) - dry-container (0.9.0) - concurrent-ruby (~> 1.0) - dry-configurable (~> 0.13, >= 0.13.0) - dry-core (0.7.1) + diff-lcs (1.5.1) + domain_name (0.6.20240107) + dry-core (1.0.1) concurrent-ruby (~> 1.0) - dry-inflector (0.2.1) - dry-logic (1.2.0) + zeitwerk (~> 2.6) + dry-inflector (1.0.0) + dry-logic (1.5.0) concurrent-ruby (~> 1.0) - dry-core (~> 0.5, >= 0.5) - dry-types (1.5.1) + dry-core (~> 1.0, < 2) + zeitwerk (~> 2.6) + dry-types (1.7.2) + bigdecimal (~> 3.0) concurrent-ruby (~> 1.0) - dry-container (~> 0.3) - dry-core (~> 0.5, >= 0.5) - dry-inflector (~> 0.1, >= 0.1.2) - dry-logic (~> 1.0, >= 1.0.2) - excon (0.92.3) - fabrication (2.28.0) - faker (2.21.0) + dry-core (~> 1.0) + dry-inflector (~> 1.0) + dry-logic (~> 1.4) + zeitwerk (~> 2.6) + excon (0.110.0) + fabrication (2.31.0) + faker (3.4.1) i18n (>= 1.8.11, < 2) - faraday (1.10.0) + faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -95,11 +95,12 @@ GEM faraday_middleware (>= 0.9) faraday_middleware (1.2.0) faraday (~> 1.0) + fiber-annotation (0.2.0) fiber-local (1.0.0) - foreman (0.87.2) - gli (2.21.0) - grape (1.7.0) - activesupport + foreman (0.88.1) + gli (2.21.1) + grape (2.0.0) + activesupport (>= 5) builder dry-types (>= 1.1) mustermann-grape (~> 1.0.0) @@ -109,48 +110,51 @@ GEM grape multi_json roar (~> 1.1.0) - grape-swagger (1.4.2) - grape (~> 1.3) - hashdiff (1.0.1) + grape-swagger (2.1.0) + grape (>= 1.7, < 3.0) + rack-test (~> 2) + hashdiff (1.1.0) hashie (3.4.6) http-accept (1.7.0) - http-cookie (1.0.5) + http-cookie (1.0.6) domain_name (~> 0.5) hyperclient (1.0.1) addressable faraday (>= 0.9.0) faraday_hal_middleware faraday_middleware - i18n (1.14.1) + i18n (1.14.5) concurrent-ruby (~> 1.0) - io-console (0.5.11) - irb (1.4.1) - reline (>= 0.3.0) - json (2.6.2) + io-console (0.7.2) + irb (1.13.1) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + json (2.7.2) kaminari-core (1.2.2) kaminari-grape (1.0.1) grape kaminari-core (~> 1.0) kgio (2.11.4) + language_server-protocol (3.17.0.3) mailchimp_api_v3 (0.2.18) rest-client (~> 2) matrix (0.4.2) - mime-types (3.4.1) + mime-types (3.5.2) mime-types-data (~> 3.2015) - mime-types-data (3.2022.0105) - mini_mime (1.1.2) - mini_portile2 (2.8.6) - minitest (5.19.0) + mime-types-data (3.2024.0604) + mini_mime (1.1.5) + mini_portile2 (2.8.7) + minitest (5.23.1) mongo (2.15.1) bson (>= 4.8.2, < 5.0.0) - mongoid (7.5.2) + mongoid (7.5.4) activemodel (>= 5.1, < 7.1, != 7.0.0) mongo (>= 2.10.5, < 3.0.0) ruby2_keywords (~> 0.0.5) - mongoid-compatibility (0.5.1) + mongoid-compatibility (0.6.0) activesupport mongoid (>= 2.0) - mongoid-scroll (0.3.7) + mongoid-scroll (1.0.1) i18n mongoid (>= 3.0) mongoid-compatibility @@ -159,39 +163,44 @@ GEM mongoid (>= 3.0.0) mongoid-compatibility multi_json (1.15.0) - multipart-post (2.2.3) - mustermann (1.1.1) + multipart-post (2.4.1) + mustermann (3.0.0) ruby2_keywords (~> 0.0.1) mustermann-grape (1.0.2) mustermann (>= 1.0.0) netrc (0.11.0) - newrelic_rpm (8.8.0) - nio4r (2.5.8) - nokogiri (1.16.5) + newrelic_rpm (9.10.2) + nio4r (2.7.3) + nokogiri (1.16.6) mini_portile2 (~> 2.8.2) racc (~> 1.4) - parallel (1.22.1) - parser (3.1.2.0) + parallel (1.25.1) + parser (3.3.3.0) ast (~> 2.4.1) - public_suffix (4.0.7) - racc (1.7.3) - rack (2.2.8.1) + racc + psych (5.1.2) + stringio + public_suffix (5.0.5) + racc (1.8.0) + rack (2.2.9) rack-accept (0.4.5) rack (>= 0.4) - rack-cors (1.1.1) + rack-cors (2.0.2) rack (>= 2.0.0) rack-rewrite (1.5.1) rack-robotz (0.0.4) rack rack-server-pages (0.1.0) rack - rack-test (1.1.0) - rack (>= 1.0, < 3) + rack-test (2.1.0) + rack (>= 1.3) rainbow (3.1.1) - raindrops (0.20.0) + raindrops (0.20.1) rake (12.3.3) - regexp_parser (2.5.0) - reline (0.3.1) + rdoc (6.7.0) + psych (>= 4.0.0) + regexp_parser (2.9.2) + reline (0.5.9) io-console (~> 0.5) representable (3.2.0) declarative (< 0.1.0) @@ -202,55 +211,59 @@ GEM http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) - rexml (3.2.8) - strscan (>= 3.0.9) + rexml (3.3.0) + strscan roar (1.1.1) representable (~> 3.0) - rspec (3.11.0) - rspec-core (~> 3.11.0) - rspec-expectations (~> 3.11.0) - rspec-mocks (~> 3.11.0) - rspec-core (3.11.0) - rspec-support (~> 3.11.0) - rspec-expectations (3.11.0) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-mocks (3.11.1) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-support (3.11.0) - rubocop (1.30.1) + rspec-support (~> 3.13.0) + rspec-support (3.13.1) + rubocop (1.64.1) + json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.1.0.0) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.18.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.18.0) - parser (>= 3.1.1.0) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) + rubocop-capybara (2.21.0) + rubocop (~> 1.41) rubocop-faker (1.1.0) faker (>= 2.12.0) rubocop (>= 0.82.0) rubocop-rake (0.6.0) rubocop (~> 1.0) - rubocop-rspec (2.11.1) - rubocop (~> 1.19) - ruby-enum (0.9.0) - i18n - ruby-progressbar (1.11.0) + rubocop-rspec (3.0.1) + rubocop (~> 1.61) + ruby-enum (1.0.0) + ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) rubyzip (2.3.2) - selenium-webdriver (4.1.0) - childprocess (>= 0.5, < 5.0) + selenium-webdriver (4.21.1) + base64 (~> 0.2) rexml (~> 3.2, >= 3.2.5) - rubyzip (>= 1.2.2) + rubyzip (>= 1.2.2, < 3.0) + websocket (~> 1.0) slack-ruby-bot (0.16.1) activesupport hashie slack-ruby-client (>= 0.14.0) - slack-ruby-bot-server (2.1.0) + slack-ruby-bot-server (2.1.1) async foreman grape @@ -261,9 +274,9 @@ GEM rack-rewrite rack-server-pages slack-ruby-client - slack-ruby-bot-server-mailchimp (0.2.0) + slack-ruby-bot-server-mailchimp (0.3.0) mailchimp_api_v3 - slack-ruby-bot-server (>= 0.10.0) + slack-ruby-bot-server (>= 2.0.1) slack-ruby-bot-server-rtm (0.2.0) async-websocket (~> 0.8.0) slack-ruby-bot (>= 0.12.0) @@ -274,6 +287,7 @@ GEM gli hashie websocket-driver + stringio (3.1.1) stripe (1.58.0) rest-client (>= 1.4, < 4.0) stripe-ruby-mock (2.4.1) @@ -282,31 +296,30 @@ GEM stripe (>= 1.31.0, <= 1.58.0) strscan (3.1.0) time_ago_in_words (0.1.1) - timecop (0.9.5) - timers (4.3.3) + timecop (0.9.10) + timers (4.3.5) trailblazer-option (0.1.2) tzinfo (2.0.6) concurrent-ruby (~> 1.0) uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.8.2) - unicode-display_width (2.1.0) + unicode-display_width (2.5.0) unicorn (6.1.0) kgio (~> 2.6) raindrops (~> 0.7) - vcr (6.1.0) + vcr (6.2.0) wannabe_bool (0.7.1) - webmock (3.14.0) + webmock (3.23.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.7.0) - websocket-driver (0.7.5) + webrick (1.8.1) + websocket (1.2.10) + websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) + zeitwerk (2.6.16) PLATFORMS ruby @@ -317,9 +330,10 @@ DEPENDENCIES excon fabrication faker + faraday_middleware foreman hashie (= 3.4.6) - hyperclient + hyperclient (~> 1.0.0) irb json kaminari-grape @@ -330,6 +344,7 @@ DEPENDENCIES mongoid-shell multi_json newrelic_rpm + rack (~> 2) rack-robotz rack-server-pages rack-test @@ -337,6 +352,7 @@ DEPENDENCIES roar rspec rubocop + rubocop-capybara rubocop-faker rubocop-rake rubocop-rspec diff --git a/config/initializers/slack_ruby_bot_client.rb b/config/initializers/slack_ruby_bot_client.rb index 7f6526ed..ded87dd2 100644 --- a/config/initializers/slack_ruby_bot_client.rb +++ b/config/initializers/slack_ruby_bot_client.rb @@ -5,7 +5,7 @@ class Client < Slack::RealTime::Client attr_accessor :send_gifs, :aliases def initialize(attrs = {}) - super(attrs) + super @send_gifs = attrs[:send_gifs] @aliases = attrs[:aliases] end diff --git a/slack-gamebot/api/helpers/sort_helpers.rb b/slack-gamebot/api/helpers/sort_helpers.rb index 2ffc0814..aebf6bc9 100644 --- a/slack-gamebot/api/helpers/sort_helpers.rb +++ b/slack-gamebot/api/helpers/sort_helpers.rb @@ -44,7 +44,7 @@ def sort(coll, options = {}) error!("Cannot sort #{coll.class.name}", 500) end end - coll = coll.is_a?(Module) && coll.respond_to?(:all) ? coll.all : coll + coll.is_a?(Module) && coll.respond_to?(:all) ? coll.all : coll end end end diff --git a/slack-gamebot/api/presenters/user_presenter.rb b/slack-gamebot/api/presenters/user_presenter.rb index a44614d7..c43e3673 100644 --- a/slack-gamebot/api/presenters/user_presenter.rb +++ b/slack-gamebot/api/presenters/user_presenter.rb @@ -11,7 +11,7 @@ module UserPresenter property :wins, type: Integer, desc: 'Number of wins.' property :losses, type: Integer, desc: 'Number of losses.' property :elo, type: Integer, desc: 'Elo.' - property :elo_history, type: Array[Integer], desc: 'Elo history.' + property :elo_history, type: [Integer], desc: 'Elo history.' property :rank, type: Integer, desc: 'Rank.' property :winning_streak, type: Integer, desc: 'Longest winning streak this season.' property :losing_streak, type: Integer, desc: 'Longest losing streak this season.' diff --git a/slack-gamebot/server.rb b/slack-gamebot/server.rb index fca594e5..c0f8442a 100644 --- a/slack-gamebot/server.rb +++ b/slack-gamebot/server.rb @@ -3,7 +3,7 @@ class Server < SlackRubyBotServer::RealTime::Server def initialize(attrs = {}) attrs = attrs.dup attrs[:aliases] = ([attrs[:team].game.name] + [attrs[:team].aliases]).flatten.compact - super attrs + super end on :user_change do |client, data| diff --git a/spec/api/cors_spec.rb b/spec/api/cors_spec.rb index b85fde69..25cd9fa4 100644 --- a/spec/api/cors_spec.rb +++ b/spec/api/cors_spec.rb @@ -14,11 +14,13 @@ expect(last_response.headers['Access-Control-Allow-Origin']).to eq '*' expect(last_response.headers['Access-Control-Expose-Headers']).to eq '' end + it 'includes Access-Control-Allow-Origin in the response' do get '/api/', {}, 'HTTP_ORIGIN' => 'http://cors.example.com' expect(last_response.status).to eq 200 expect(last_response.headers['Access-Control-Allow-Origin']).to eq '*' end + it 'includes Access-Control-Allow-Origin in errors' do get '/api/invalid', {}, 'HTTP_ORIGIN' => 'http://cors.example.com' expect(last_response.status).to eq 404 diff --git a/spec/api/endpoints/challenges_endpoint_spec.rb b/spec/api/endpoints/challenges_endpoint_spec.rb index d627c170..f2d7f8e6 100644 --- a/spec/api/endpoints/challenges_endpoint_spec.rb +++ b/spec/api/endpoints/challenges_endpoint_spec.rb @@ -21,12 +21,14 @@ context 'challenge' do let(:existing_challenge) { Fabricate(:challenge) } + it 'returns a challenge' do challenge = client.challenge(id: existing_challenge.id) expect(challenge.id).to eq existing_challenge.id.to_s expect(challenge._links.self._url).to eq "http://example.org/api/challenges/#{existing_challenge.id}" expect(challenge._links.team._url).to eq "http://example.org/api/teams/#{existing_challenge.team.id}" end + it 'cannot return a challenge for team with api off' do team.update_attributes!(api: false) expect { client.challenge(id: existing_challenge.id).resource }.to raise_error Faraday::ClientError do |e| @@ -38,10 +40,12 @@ context 'doubles challenge' do let(:existing_challenge) { Fabricate(:doubles_challenge) } + before do existing_challenge.accept!(existing_challenge.challenged.first) existing_challenge.lose!(existing_challenge.challengers.first) end + it 'returns a challenge with links to challengers, challenged and played match' do challenge = client.challenge(id: existing_challenge.id) expect(challenge.id).to eq existing_challenge.id.to_s diff --git a/spec/api/endpoints/credit_cards_endpoint_spec.rb b/spec/api/endpoints/credit_cards_endpoint_spec.rb index 89aacd8a..bda13ec3 100644 --- a/spec/api/endpoints/credit_cards_endpoint_spec.rb +++ b/spec/api/endpoints/credit_cards_endpoint_spec.rb @@ -11,8 +11,10 @@ expect(json['type']).to eq 'param_error' end end + context 'subscribed team without a stripe customer id' do let!(:team) { Fabricate(:team, subscribed: true, stripe_customer_id: nil) } + it 'fails to update credit_card' do expect do client.credit_cards._post( @@ -25,9 +27,11 @@ end end end + context 'existing subscribed team' do - include_context :stripe_mock + include_context 'stripe mock' let!(:team) { Fabricate(:team) } + before do stripe_helper.create_plan(id: 'slack-playplay-yearly', amount: 2999) customer = Stripe::Customer.create( @@ -38,6 +42,7 @@ expect_any_instance_of(Team).to receive(:inform!).once team.update_attributes!(subscribed: true, stripe_customer_id: customer['id']) end + it 'updates a credit card' do new_source = stripe_helper.generate_card_token client.credit_cards._post( diff --git a/spec/api/endpoints/games_endpoint_spec.rb b/spec/api/endpoints/games_endpoint_spec.rb index 2f72d44b..ac1de301 100644 --- a/spec/api/endpoints/games_endpoint_spec.rb +++ b/spec/api/endpoints/games_endpoint_spec.rb @@ -7,6 +7,7 @@ context 'game' do let(:existing_game) { Fabricate(:game) } + it 'returns a game' do game = client.game(id: existing_game.id) expect(game.id).to eq existing_game.id.to_s @@ -16,6 +17,7 @@ context 'game' do let(:existing_game) { Fabricate(:game) } + it 'returns a game with links to teams' do game = client.game(id: existing_game.id) expect(game.id).to eq existing_game.id.to_s diff --git a/spec/api/endpoints/matches_endpoint_spec.rb b/spec/api/endpoints/matches_endpoint_spec.rb index f099e253..bde83637 100644 --- a/spec/api/endpoints/matches_endpoint_spec.rb +++ b/spec/api/endpoints/matches_endpoint_spec.rb @@ -21,11 +21,13 @@ context 'match' do let(:existing_match) { Fabricate(:match, team: team) } + it 'returns a match' do match = client.match(id: existing_match.id) expect(match.id).to eq existing_match.id.to_s expect(match._links.self._url).to eq "http://example.org/api/matches/#{existing_match.id}" end + it 'cannot return a match for a team with api off' do team.update_attributes!(api: false) expect { client.match(id: existing_match.id).resource }.to raise_error Faraday::ClientError do |e| @@ -37,6 +39,7 @@ context 'match' do let(:existing_match) { Fabricate(:match) } + it 'returns a match with links to challenge' do match = client.match(id: existing_match.id) expect(match.id).to eq existing_match.id.to_s diff --git a/spec/api/endpoints/root_endpoint_spec.rb b/spec/api/endpoints/root_endpoint_spec.rb index ac9e2b9c..ada9b8d4 100644 --- a/spec/api/endpoints/root_endpoint_spec.rb +++ b/spec/api/endpoints/root_endpoint_spec.rb @@ -9,6 +9,7 @@ links = JSON.parse(last_response.body)['_links'] expect(links.keys.sort).to eq(%w[self status team teams user users challenge challenges credit_cards match matches current_season season seasons subscriptions game games].sort) end + it 'follows all links' do get '/api/' expect(last_response.status).to eq 200 @@ -21,9 +22,10 @@ get href.gsub('http://example.org', '') expect(last_response.status).to eq 200 - expect(JSON.parse(last_response.body)).to_not eq({}) + expect(JSON.parse(last_response.body)).not_to eq({}) end end + it 'rewrites encoded HAL links to make them clickable' do get '/api/teams/%7B?cursor,size%7D' expect(last_response.status).to eq 302 diff --git a/spec/api/endpoints/seasons_endpoint_spec.rb b/spec/api/endpoints/seasons_endpoint_spec.rb index 1b8a0d8a..d8458473 100644 --- a/spec/api/endpoints/seasons_endpoint_spec.rb +++ b/spec/api/endpoints/seasons_endpoint_spec.rb @@ -21,11 +21,13 @@ context 'season' do let(:existing_season) { Fabricate(:season) } + it 'returns a season' do season = client.season(id: existing_season.id) expect(season.id).to eq existing_season.id.to_s expect(season._links.self._url).to eq "http://example.org/api/seasons/#{existing_season.id}" end + it 'cannot return a season for a team with api off' do team.update_attributes!(api: false) expect { client.season(id: existing_season.id).resource }.to raise_error Faraday::ClientError do |e| @@ -39,11 +41,13 @@ before do Fabricate(:match) end + it 'returns the current season' do season = client.current_season(team_id: team.id.to_s) expect(season.id).to eq 'current' expect(season._links.self._url).to eq 'http://example.org/api/seasons/current' end + it 'cannot return the current season for team with api off' do team.update_attributes!(api: false) expect { client.current_season(team_id: team.id.to_s).resource }.to raise_error Faraday::ClientError do |e| diff --git a/spec/api/endpoints/status_endpoint_spec.rb b/spec/api/endpoints/status_endpoint_spec.rb index e7234980..563f5d8b 100644 --- a/spec/api/endpoints/status_endpoint_spec.rb +++ b/spec/api/endpoints/status_endpoint_spec.rb @@ -15,6 +15,7 @@ context 'with a team that is inactive' do let!(:team) { Fabricate(:team, api: true, active: false) } + it 'returns a status' do status = client.status expect(status.games_count).to eq 1 @@ -27,9 +28,11 @@ context 'with a team that has an inactive account' do let!(:team) { Fabricate(:team, api: true, active: true) } + before do expect_any_instance_of(Team).to receive(:ping!) { raise Slack::Web::Api::Errors::SlackError, 'account_inactive' } end + it 'returns a status and deactivates team' do status = client.status expect(status.games_count).to eq 1 @@ -43,6 +46,7 @@ context 'with a team with api off' do let!(:team) { Fabricate(:team, api: false) } + it 'returns total counts anyway' do status = client.status expect(status.games_count).to eq 1 diff --git a/spec/api/endpoints/subscriptions_endpoint_spec.rb b/spec/api/endpoints/subscriptions_endpoint_spec.rb index a07b1b09..9e401ba0 100644 --- a/spec/api/endpoints/subscriptions_endpoint_spec.rb +++ b/spec/api/endpoints/subscriptions_endpoint_spec.rb @@ -11,8 +11,10 @@ expect(json['type']).to eq 'param_error' end end + context 'subscribed team' do let!(:team) { Fabricate(:team, subscribed: true) } + it 'fails to create a subscription' do expect do client.subscriptions._post( @@ -27,18 +29,22 @@ end end end + context 'team with a canceled subscription' do let!(:team) { Fabricate(:team, subscribed: false, stripe_customer_id: 'customer_id') } let(:stripe_customer) { double(Stripe::Customer) } + before do allow(Stripe::Customer).to receive(:retrieve).with(team.stripe_customer_id).and_return(stripe_customer) end + context 'with an active subscription' do before do allow(stripe_customer).to receive(:subscriptions).and_return([ double(Stripe::Subscription) ]) end + it 'fails to create a subscription' do expect do client.subscriptions._post( @@ -53,10 +59,12 @@ end end end + context 'without no active subscription' do before do allow(stripe_customer).to receive(:subscriptions).and_return([]) end + it 'updates a subscription' do expect(Stripe::Customer).to receive(:update).with( team.stripe_customer_id, @@ -83,14 +91,16 @@ ) team.reload expect(team.subscribed).to be true - expect(team.subscribed_at).to_not be nil + expect(team.subscribed_at).not_to be_nil expect(team.stripe_customer_id).to eq 'customer_id' end end end + context 'existing team' do - include_context :stripe_mock + include_context 'stripe mock' let!(:team) { Fabricate(:team) } + context 'with a plan' do before do expect_any_instance_of(Team).to receive(:inform!).once @@ -103,11 +113,12 @@ ) team.reload end + it 'creates a subscription' do expect(team.subscribed).to be true - expect(team.stripe_customer_id).to_not be_blank + expect(team.stripe_customer_id).not_to be_blank customer = Stripe::Customer.retrieve(team.stripe_customer_id) - expect(customer).to_not be nil + expect(customer).not_to be_nil expect(customer.metadata.to_h).to eq( id: team._id.to_s, name: team.name, @@ -115,11 +126,12 @@ domain: team.domain, game: team.game.name ) - expect(customer.discount).to be nil + expect(customer.discount).to be_nil subscriptions = customer.subscriptions expect(subscriptions.count).to eq 1 end end + context 'with a coupon' do before do expect_any_instance_of(Team).to receive(:inform!).once @@ -134,11 +146,12 @@ ) team.reload end + it 'creates a subscription' do expect(team.subscribed).to be true - expect(team.stripe_customer_id).to_not be_blank + expect(team.stripe_customer_id).not_to be_blank customer = Stripe::Customer.retrieve(team.stripe_customer_id) - expect(customer).to_not be nil + expect(customer).not_to be_nil subscriptions = customer.subscriptions expect(subscriptions.count).to eq 1 discount = customer.discount diff --git a/spec/api/endpoints/teams_endpoint_spec.rb b/spec/api/endpoints/teams_endpoint_spec.rb index 03a5eb07..5f76d082 100644 --- a/spec/api/endpoints/teams_endpoint_spec.rb +++ b/spec/api/endpoints/teams_endpoint_spec.rb @@ -3,14 +3,15 @@ describe Api::Endpoints::TeamsEndpoint do include Api::Test::EndpointTest + let!(:game) { Fabricate(:game) } + context 'cursor' do it_behaves_like 'a cursor api', Team end - let!(:game) { Fabricate(:game) } - context 'team' do let(:existing_team) { Fabricate(:team, game: game) } + it 'returns a team' do team = client.team(id: existing_team.id) expect(team.id).to eq existing_team.id.to_s @@ -22,45 +23,55 @@ context 'active/inactive' do let!(:active_team) { Fabricate(:team, game: game, active: true) } let!(:inactive_team) { Fabricate(:team, game: game, active: false) } + it 'returns all teams' do teams = client.teams expect(teams.count).to eq 2 end + it 'returns active teams' do teams = client.teams(active: true) expect(teams.count).to eq 1 expect(teams.to_a.first.team_id).to eq active_team.team_id end end + context 'game_id' do let!(:team1) { Fabricate(:team, game: game) } let!(:team2) { Fabricate(:team, game: Fabricate(:game)) } + it 'returns all teams' do teams = client.teams expect(teams.count).to eq 2 end + it 'returns team by game' do teams = client.teams(game_id: game.id.to_s) expect(teams.count).to eq 1 expect(teams.to_a.first.team_id).to eq team1.team_id end end + context 'api on/off' do let!(:team_api_on) { Fabricate(:team, game: game) } let!(:team_api_off) { Fabricate(:team, api: false, game: game) } + it 'only returns teams with api on' do teams = client.teams expect(teams.count).to eq 1 expect(teams.to_a.first.team_id).to eq team_api_on.team_id end end + context 'game_id' do let!(:team1) { Fabricate(:team, game: game) } let!(:team2) { Fabricate(:team, game: Fabricate(:game)) } + it 'returns all teams' do teams = client.teams expect(teams.count).to eq 2 end + it 'returns team by game' do teams = client.teams(game_id: game.id.to_s) expect(teams.count).to eq 1 @@ -71,6 +82,7 @@ context 'team' do let(:existing_team) { Fabricate(:team, game: game) } + it 'returns a team with links to challenges, users and matches' do team = client.team(id: existing_team.id) expect(team.id).to eq existing_team.id.to_s @@ -96,7 +108,7 @@ expect(json['message']).to eq 'Invalid parameters.' expect(json['detail']).to eq('game, game_id' => ['are missing, exactly one parameter must be provided']) end - end.to_not change(Team, :count) + end.not_to change(Team, :count) end it 'requires code' do @@ -127,6 +139,7 @@ ) ).and_return(oauth_access) end + it 'creates a team with game name' do expect_any_instance_of(Team).to receive(:activated!) expect(SlackRubyBotServer::Service.instance).to receive(:start!) @@ -142,6 +155,7 @@ expect(team.activated_user_id).to eq 'activated_user_id' end.to change(Team, :count).by(1) end + it 'creates a team with game id' do expect_any_instance_of(Team).to receive(:activated!) expect(SlackRubyBotServer::Service.instance).to receive(:start!) @@ -157,6 +171,7 @@ expect(team.activated_user_id).to eq 'activated_user_id' end.to change(Team, :count).by(1) end + it 'reactivates a deactivated team' do allow_any_instance_of(Team).to receive(:activated!) expect(SlackRubyBotServer::Service.instance).to receive(:start!) @@ -172,8 +187,9 @@ expect(team.aliases).to eq %w[foo bar] expect(team.bot_user_id).to eq 'bot_user_id' expect(team.activated_user_id).to eq 'activated_user_id' - end.to_not change(Team, :count) + end.not_to change(Team, :count) end + it 'reactivates a team deactivated on slack' do allow_any_instance_of(Team).to receive(:activated!) expect(SlackRubyBotServer::Service.instance).to receive(:start!) @@ -190,8 +206,9 @@ expect(team.aliases).to eq %w[foo bar] expect(team.bot_user_id).to eq 'bot_user_id' expect(team.activated_user_id).to eq 'activated_user_id' - end.to_not change(Team, :count) + end.not_to change(Team, :count) end + it 'updates a reactivated team with a new token' do allow_any_instance_of(Team).to receive(:activated!) expect(SlackRubyBotServer::Service.instance).to receive(:start!) @@ -206,8 +223,9 @@ expect(team.active).to be true expect(team.bot_user_id).to eq 'bot_user_id' expect(team.activated_user_id).to eq 'activated_user_id' - end.to_not change(Team, :count) + end.not_to change(Team, :count) end + it 'revives a dead team' do allow_any_instance_of(Team).to receive(:activated!) expect(SlackRubyBotServer::Service.instance).to receive(:start!) @@ -223,20 +241,22 @@ expect(team.aliases).to eq %w[foo bar] expect(team.bot_user_id).to eq 'bot_user_id' expect(team.activated_user_id).to eq 'activated_user_id' - expect(team.dead_at).to be nil - end.to_not change(Team, :count) + expect(team.dead_at).to be_nil + end.not_to change(Team, :count) end + it 'cannot switch games' do - expect(SlackRubyBotServer::Service.instance).to_not receive(:start!) + expect(SlackRubyBotServer::Service.instance).not_to receive(:start!) Fabricate(:team, game: Fabricate(:game), token: 'token', active: false) expect { client.teams._post(code: 'code', game_id: game.id.to_s) }.to raise_error Faraday::ClientError do |e| json = JSON.parse(e.response[:body]) expect(json['error']).to eq 'Invalid Game' end end + it 'returns a useful error when team already exists' do allow_any_instance_of(Team).to receive(:activated!) - expect(SlackRubyBotServer::Service.instance).to_not receive(:start!) + expect(SlackRubyBotServer::Service.instance).not_to receive(:start!) expect_any_instance_of(Team).to receive(:ping_if_active!) existing_team = Fabricate(:team, game: game, token: 'token') expect { client.teams._post(code: 'code', game: game.name) }.to raise_error Faraday::ClientError do |e| @@ -244,6 +264,7 @@ expect(json['message']).to eq "Team #{existing_team.name} is already registered." end end + context 'with mailchimp settings' do before do SlackRubyBotServer::Mailchimp.configure do |config| @@ -251,8 +272,13 @@ config.mailchimp_list_id = 'list-id' end end + after do SlackRubyBotServer::Mailchimp.config.reset! + ENV.delete('MAILCHIMP_API_KEY') + ENV.delete('MAILCHIMP_LIST_ID') + ENV.delete('MAILCHIMP_API_KEY') + ENV.delete('MAILCHIMP_LIST_ID') end let(:list) { double(Mailchimp::List, members: double(Mailchimp::List::Members)) } @@ -292,10 +318,6 @@ client.teams._post(code: 'code', game: game.name) end - after do - ENV.delete('MAILCHIMP_API_KEY') - ENV.delete('MAILCHIMP_LIST_ID') - end end end @@ -331,7 +353,7 @@ expect(team.aliases).to eq %w[foo bar] expect(team.bot_user_id).to eq 'bot_user_id' expect(team.activated_user_id).to eq 'activated_user_id' - end.to_not change(Team, :count) + end.not_to change(Team, :count) end end end diff --git a/spec/api/endpoints/users_endpoint_spec.rb b/spec/api/endpoints/users_endpoint_spec.rb index be22d3e7..7c7eb67f 100644 --- a/spec/api/endpoints/users_endpoint_spec.rb +++ b/spec/api/endpoints/users_endpoint_spec.rb @@ -13,12 +13,14 @@ context 'user' do let(:existing_user) { Fabricate(:user) } + it 'returns a user' do user = client.user(id: existing_user.id) expect(user.id).to eq existing_user.id.to_s expect(user.user_name).to eq existing_user.user_name expect(user._links.self._url).to eq "http://example.org/api/users/#{existing_user.id}" end + it 'cannot return a user for a team with api off' do team.update_attributes!(api: false) expect { client.user(id: existing_user.id).resource }.to raise_error Faraday::ClientError do |e| @@ -32,6 +34,7 @@ let!(:user_elo1) { Fabricate(:user, elo: 1, wins: 1, team: team, captain: true) } let!(:user_elo3) { Fabricate(:user, elo: 3, wins: 3, team: team) } let!(:user_elo2) { Fabricate(:user, elo: 2, wins: 2, team: team) } + it 'cannot return users for a team with api off' do team.update_attributes!(api: false) expect { client.users(team_id: team.id).resource }.to raise_error Faraday::ClientError do |e| @@ -39,22 +42,27 @@ expect(json['error']).to eq 'Not Found' end end + it 'returns users sorted by elo' do users = client.users(team_id: team.id, sort: 'elo') expect(users.map(&:id)).to eq [user_elo1, user_elo2, user_elo3].map(&:id).map(&:to_s) end + it 'returns users sorted by -elo' do users = client.users(team_id: team.id, sort: '-elo') expect(users.map(&:id)).to eq [user_elo3, user_elo2, user_elo1].map(&:id).map(&:to_s) end + it 'returns users sorted by rank' do users = client.users(team_id: team.id, sort: 'rank') expect(users.map(&:id)).to eq [user_elo3, user_elo2, user_elo1].map(&:id).map(&:to_s) end + it 'returns users sorted by -rank' do users = client.users(team_id: team.id, sort: '-rank') expect(users.map(&:id)).to eq [user_elo1, user_elo2, user_elo3].map(&:id).map(&:to_s) end + it 'returns captains' do users = client.users(team_id: team.id, captain: true) expect(users.map(&:id)).to eq [user_elo1].map(&:id).map(&:to_s) diff --git a/spec/api/swagger_documentation_spec.rb b/spec/api/swagger_documentation_spec.rb index 33ad899c..b6e62233 100644 --- a/spec/api/swagger_documentation_spec.rb +++ b/spec/api/swagger_documentation_spec.rb @@ -8,6 +8,7 @@ get '/api/swagger_doc' JSON.parse(last_response.body) end + it 'documents root level apis' do expect(subject['paths'].keys.sort).to eq [ '/api/status', @@ -35,6 +36,7 @@ get '/api/swagger_doc/users' JSON.parse(last_response.body) end + it 'documents users apis' do expect(subject['paths'].keys.sort).to eq [ '/api/users/{id}', diff --git a/spec/fabricators/season_fabricator.rb b/spec/fabricators/season_fabricator.rb index 3424a9fb..14dea421 100644 --- a/spec/fabricators/season_fabricator.rb +++ b/spec/fabricators/season_fabricator.rb @@ -1,6 +1,6 @@ Fabricator(:season) do team { Team.first || Fabricate(:team) } - before_validation do + before_create do Fabricate(:match, team: team) if Challenge.current.none? end end diff --git a/spec/initializers/array_spec.rb b/spec/initializers/array_spec.rb index 1238a325..c8eeb55c 100644 --- a/spec/initializers/array_spec.rb +++ b/spec/initializers/array_spec.rb @@ -1,39 +1,48 @@ require 'spec_helper' describe Array do - context '.and' do + describe '.and' do it 'one' do expect(['foo'].and).to eq 'foo' end + it 'two' do expect(%w[foo bar].and).to eq 'foo and bar' end + it 'three' do expect(%w[foo bar baz].and).to eq 'foo, bar and baz' end end - context '.or' do + + describe '.or' do it 'one' do expect(['foo'].or).to eq 'foo' end + it 'two' do expect(%w[foo bar].or).to eq 'foo or bar' end + it 'three' do expect(%w[foo bar baz].or).to eq 'foo, bar or baz' end end - context '.same?' do + + describe '.same?' do it 'empty' do expect([].same?).to be false end + it 'one' do expect([1].same?).to be true end + it 'two' do expect([1, 1].same?).to be true expect([1, 2].same?).to be false end + it 'three' do expect([2, 2, 2].same?).to be true expect([1, 2, 3].same?).to be false diff --git a/spec/initializers/giphy_spec.rb b/spec/initializers/giphy_spec.rb index 437b2760..08281987 100644 --- a/spec/initializers/giphy_spec.rb +++ b/spec/initializers/giphy_spec.rb @@ -1,13 +1,15 @@ require 'spec_helper' -describe Giphy, js: true, type: :feature do +describe Giphy, :js, type: :feature do context 'with GIPHY_API_KEY' do before do ENV['GIPHY_API_KEY'] = 'key' end + after do ENV.delete('GIPHY_API_KEY') end + it 'returns a random gif', vcr: { cassette_name: 'giphy_random' } do expect(Giphy.random('bot')).to eq 'https://media4.giphy.com/media/QAPGorQJSoarrsjVhH/200.gif' end diff --git a/spec/integration/index_spec.rb b/spec/integration/index_spec.rb index c304b308..98b1d253 100644 --- a/spec/integration/index_spec.rb +++ b/spec/integration/index_spec.rb @@ -1,10 +1,12 @@ require 'spec_helper' -describe 'index.html', js: true, type: :feature do +describe 'index.html', :js, type: :feature do let!(:game) { Fabricate(:game, name: 'pong') } + before do visit '/' end + it 'includes a link to add to slack with the client id' do expect(title).to eq('PlayPlay.io - Ping Pong Bot, Chess Bot, Pool Bot and Tic Tac Toe Bot for Slack') click_link 'Add to Slack' diff --git a/spec/integration/privacy_spec.rb b/spec/integration/privacy_spec.rb index 7febaae7..05cf9edd 100644 --- a/spec/integration/privacy_spec.rb +++ b/spec/integration/privacy_spec.rb @@ -1,9 +1,10 @@ require 'spec_helper' -describe 'privacy.html', js: true, type: :feature do +describe 'privacy.html', :js, type: :feature do before do visit '/privacy' end + it 'displays privacy.html page' do expect(title).to eq('PlayPlay.io - Privacy Policy') end diff --git a/spec/integration/subscribe_spec.rb b/spec/integration/subscribe_spec.rb index 1b90bf1d..5258d174 100644 --- a/spec/integration/subscribe_spec.rb +++ b/spec/integration/subscribe_spec.rb @@ -1,45 +1,54 @@ require 'spec_helper' -describe 'Subscribe', js: true, type: :feature do +describe 'Subscribe', :js, type: :feature do let!(:game) { Fabricate(:game, name: 'pong') } + context 'without team_id' do before do visit '/subscribe' end + it 'requires a team' do - expect(find('#messages')).to have_text('Missing or invalid team ID and/or game.') - find('#subscribe', visible: false) + expect(find_by_id('messages')).to have_text('Missing or invalid team ID and/or game.') + find_by_id('subscribe', visible: false) end end + context 'without game' do let!(:team) { Fabricate(:team) } + before do visit "/subscribe?team_id=#{team.team_id}" end + it 'requires a game' do - expect(find('#messages')).to have_text('Missing or invalid team ID and/or game.') - find('#subscribe', visible: false) + expect(find_by_id('messages')).to have_text('Missing or invalid team ID and/or game.') + find_by_id('subscribe', visible: false) end end + context 'for a subscribed team' do let!(:team) { Fabricate(:team, game: game, subscribed: true) } + before do visit "/subscribe?team_id=#{team.team_id}&game=#{team.game.name}" end + it 'displays an error' do - expect(find('#messages')).to have_text("Team #{team.name} is already subscribed to #{team.game.name}, thank you.") - find('#subscribe', visible: false) + expect(find_by_id('messages')).to have_text("Team #{team.name} is already subscribed to #{team.game.name}, thank you.") + find_by_id('subscribe', visible: false) end end + shared_examples 'subscribes' do it 'subscribes' do visit "/subscribe?team_id=#{team.team_id}&game=#{team.game.name}" - expect(find('#messages')).to have_text("Subscribe team #{team.name} to #{team.game.name} for $29.99 a year!") - find('#subscribe', visible: true) + expect(find_by_id('messages')).to have_text("Subscribe team #{team.name} to #{team.game.name} for $29.99 a year!") + find_by_id('subscribe', visible: true) expect(Stripe::Customer).to receive(:create).and_return('id' => 'customer_id') - find('#subscribeButton').click + find_by_id('subscribeButton').click sleep 1 expect_any_instance_of(Team).to receive(:inform!).with(Team::SUBSCRIBED_TEXT, 'thanks') @@ -55,49 +64,59 @@ sleep 5 - expect(find('#messages')).to have_text("Team #{team.name} successfully subscribed to #{team.game.name}.\nThank you!") - find('#subscribe', visible: false) + expect(find_by_id('messages')).to have_text("Team #{team.name} successfully subscribed to #{team.game.name}.\nThank you!") + find_by_id('subscribe', visible: false) team.reload expect(team.subscribed).to be true expect(team.stripe_customer_id).to eq 'customer_id' end end + context 'with a stripe key' do before do ENV['STRIPE_API_PUBLISHABLE_KEY'] = 'pk_test_804U1vUeVeTxBl8znwriXskf' end + after do ENV.delete 'STRIPE_API_PUBLISHABLE_KEY' end + context 'a team' do let!(:team) { Fabricate(:team, game: game) } + it_behaves_like 'subscribes' end + context 'a team with two games' do let!(:team) { Fabricate(:team, game: game) } let!(:team2) { Fabricate(:team, team_id: team.team_id, game: Fabricate(:game)) } + it_behaves_like 'subscribes' end + context 'a second team with two games' do let!(:team2) { Fabricate(:team, game: Fabricate(:game)) } let!(:team) { Fabricate(:team, game: game, team_id: team2.team_id) } + it_behaves_like 'subscribes' end + context 'with a coupon' do let!(:team) { Fabricate(:team, game: game) } + it 'applies the coupon' do coupon = double(Stripe::Coupon, id: 'coupon-id', amount_off: 1200) expect(Stripe::Coupon).to receive(:retrieve).with('coupon-id').and_return(coupon) visit "/subscribe?team_id=#{team.team_id}&game=#{team.game.name}&coupon=coupon-id" - expect(find('#messages')).to have_text("Subscribe team #{team.name} to #{team.game.name} for $17.99 for the first year and $29.99 thereafter with coupon coupon-id!") - find('#subscribe', visible: true) + expect(find_by_id('messages')).to have_text("Subscribe team #{team.name} to #{team.game.name} for $17.99 for the first year and $29.99 thereafter with coupon coupon-id!") + find_by_id('subscribe', visible: true) expect(Stripe::Customer).to receive(:create).with(hash_including(coupon: 'coupon-id')).and_return('id' => 'customer_id') expect_any_instance_of(Team).to receive(:inform!).with(Team::SUBSCRIBED_TEXT, 'thanks') - find('#subscribeButton').click + find_by_id('subscribeButton').click sleep 1 stripe_iframe = all('iframe[name=stripe_checkout_app]').last @@ -111,8 +130,8 @@ sleep 5 - expect(find('#messages')).to have_text("Team #{team.name} successfully subscribed to #{team.game.name}.\nThank you!") - find('#subscribe', visible: false) + expect(find_by_id('messages')).to have_text("Team #{team.name} successfully subscribed to #{team.game.name}.\nThank you!") + find_by_id('subscribe', visible: false) team.reload expect(team.subscribed).to be true diff --git a/spec/integration/teams_spec.rb b/spec/integration/teams_spec.rb index 2d7b11c4..89ee2edc 100644 --- a/spec/integration/teams_spec.rb +++ b/spec/integration/teams_spec.rb @@ -1,10 +1,11 @@ require 'spec_helper' -describe 'Teams', js: true, type: :feature do +describe 'Teams', :js, type: :feature do context 'oauth', vcr: { cassette_name: 'auth_test' } do before do Fabricate(:game, name: 'pong') end + it 'registers a team' do allow_any_instance_of(Team).to receive(:ping!).and_return(ok: true) expect(SlackRubyBotServer::Service.instance).to receive(:start!) @@ -12,7 +13,7 @@ allow_any_instance_of(Slack::Web::Client).to receive(:oauth_access).with(hash_including(code: 'code')).and_return(oauth_access) expect do visit '/?code=code&game=pong' - expect(page.find('#messages')).to have_content 'Team successfully registered!' + expect(page.find_by_id('messages')).to have_content 'Team successfully registered!' end.to change(Team, :count).by(1) end end diff --git a/spec/integration/update_cc_spec.rb b/spec/integration/update_cc_spec.rb index b7b719a0..3f7fd7c1 100644 --- a/spec/integration/update_cc_spec.rb +++ b/spec/integration/update_cc_spec.rb @@ -1,17 +1,21 @@ require 'spec_helper' -describe 'Update cc', js: true, type: :feature do +describe 'Update cc', :js, type: :feature do context 'with a stripe key' do before do ENV['STRIPE_API_PUBLISHABLE_KEY'] = 'pk_test_804U1vUeVeTxBl8znwriXskf' end + after do ENV.delete 'STRIPE_API_PUBLISHABLE_KEY' end + context 'a game' do let!(:game) { Fabricate(:game, name: 'pong') } + context 'a team with a stripe customer ID' do let!(:team) { Fabricate(:team, game: game, stripe_customer_id: 'stripe_customer_id') } + it 'updates cc' do visit "/update_cc?team_id=#{team.team_id}&game=#{team.game.name}" expect(find('h3')).to have_text('PLAYPLAY.IO: UPDATE CREDIT CARD INFO') @@ -30,11 +34,13 @@ find('button[type="submit"]').click end sleep 5 - expect(find('#messages')).to have_text("Successfully updated team #{team.name} credit card for #{team.game.name}.\nThank you!") + expect(find_by_id('messages')).to have_text("Successfully updated team #{team.name} credit card for #{team.game.name}.\nThank you!") end end + context 'a team without a stripe customer ID' do let!(:team) { Fabricate(:team, game: game, stripe_customer_id: nil) } + it 'displays error' do visit "/update_cc?team_id=#{team.team_id}&game=#{team.game.name}" expect(find('h3')).to have_text('PLAYPLAY.IO: UPDATE CREDIT CARD INFO') @@ -49,7 +55,7 @@ find('button[type="submit"]').click end sleep 5 - expect(find('#messages')).to have_text('Not a Subscriber') + expect(find_by_id('messages')).to have_text('Not a Subscriber') end end end diff --git a/spec/models/challenge_spec.rb b/spec/models/challenge_spec.rb index e3b1d9ac..f6472d44 100644 --- a/spec/models/challenge_spec.rb +++ b/spec/models/challenge_spec.rb @@ -1,56 +1,69 @@ require 'spec_helper' describe Challenge do - context '#to_s' do + describe '#to_s' do let(:challenge) { Fabricate(:challenge) } + it 'displays challenge' do expect(challenge.to_s).to eq "a challenge between #{challenge.challengers.first.user_name} and #{challenge.challenged.first.user_name}" end + context 'unregistered users' do before do challenge.challengers.first.unregister! end + it 'removes user name' do expect(challenge.to_s).to eq "a challenge between and #{challenge.challenged.first.user_name}" end end + context 'users with nickname' do before do challenge.challengers.first.update_attributes!(nickname: 'bob') end + it 'rewrites user name' do expect(challenge.to_s).to eq "a challenge between bob and #{challenge.challenged.first.user_name}" end end end + context 'find_by_user' do let(:challenge) { Fabricate(:challenge) } + it 'finds a challenge by challenger' do challenge.challengers.each do |challenger| expect(Challenge.find_by_user(challenge.team, challenge.channel, challenger)).to eq challenge end end + it 'finds a challenge by challenged' do challenge.challenged.each do |challenger| expect(Challenge.find_by_user(challenge.team, challenge.channel, challenger)).to eq challenge end end + it 'does not find a challenge on another channel' do - expect(Challenge.find_by_user(challenge.team, 'another', challenge.challengers.first)).to be nil + expect(Challenge.find_by_user(challenge.team, 'another', challenge.challengers.first)).to be_nil end end - context '#split_teammates_and_opponents', vcr: { cassette_name: 'user_info' } do + + describe '#split_teammates_and_opponents', vcr: { cassette_name: 'user_info' } do let!(:challenger) { Fabricate(:user) } let(:client) { SlackRubyBot::Client.new } + before do client.owner = challenger.team end + it 'splits a single challenge' do opponent = Fabricate(:user, user_name: 'username') challengers, opponents = Challenge.split_teammates_and_opponents(client, challenger, ['username']) expect(challengers).to eq([challenger]) expect(opponents).to eq([opponent]) end + it 'splits a double challenge' do teammate = Fabricate(:user) opponent1 = Fabricate(:user, user_name: 'username') @@ -59,6 +72,7 @@ expect(challengers).to eq([challenger, teammate]) expect(opponents).to eq([opponent1, opponent2]) end + it 'requires known opponents' do allow(client.web_client).to receive(:users_info) expect do @@ -66,57 +80,69 @@ end.to raise_error SlackGamebot::Error, "I don't know who username is! Ask them to _register_." end end - context '#create_from_teammates_and_opponents!' do + + describe '#create_from_teammates_and_opponents!' do let!(:challenger) { Fabricate(:user) } let(:teammate) { Fabricate(:user) } let(:opponent) { Fabricate(:user) } let(:client) { SlackRubyBot::Client.new } + before do client.owner = challenger.team end + it 'requires an opponent' do expect do Challenge.create_from_teammates_and_opponents!(client, 'channel', challenger, []) end.to raise_error Mongoid::Errors::Validations, /Number of teammates \(1\) and opponents \(0\) must match./ end + it 'requires the same number of opponents' do expect do Challenge.create_from_teammates_and_opponents!(client, 'channel', challenger, [opponent.slack_mention, 'with', teammate.slack_mention]) end.to raise_error Mongoid::Errors::Validations, /Number of teammates \(2\) and opponents \(1\) must match./ end + context 'with unbalanced option enabled' do before do challenger.team.update_attributes!(unbalanced: true) end + it 'requires an opponent' do expect do Challenge.create_from_teammates_and_opponents!(client, 'channel', challenger, []) end.to raise_error Mongoid::Errors::Validations, /Number of teammates \(1\) and opponents \(0\) must match./ end + it 'does not requires the same number of opponents' do expect do Challenge.create_from_teammates_and_opponents!(client, 'channel', challenger, [opponent.slack_mention, 'with', teammate.slack_mention]) - end.to_not raise_error + end.not_to raise_error end end + it 'requires another opponent' do expect do Challenge.create_from_teammates_and_opponents!(client, 'channel', challenger, [challenger.slack_mention]) end.to raise_error Mongoid::Errors::Validations, /#{challenger.user_name} cannot play against themselves./ end + it 'uniques opponents mentioned multiple times' do expect do Challenge.create_from_teammates_and_opponents!(client, 'channel', challenger, [opponent.slack_mention, opponent.slack_mention, 'with', teammate.slack_mention]) end.to raise_error Mongoid::Errors::Validations, /Number of teammates \(2\) and opponents \(1\) must match./ end + context 'with another singles proposed challenge' do let(:challenge) { Fabricate(:challenge) } + it 'cannot create a duplicate challenge for the challenger' do existing_challenger = challenge.challengers.first expect do Challenge.create_from_teammates_and_opponents!(client, challenge.channel, challenger, [existing_challenger.slack_mention]) end.to raise_error Mongoid::Errors::Validations, /#{existing_challenger.user_name} can't play./ end + it 'cannot create a duplicate challenge for the challenge' do existing_challenger = challenge.challenged.first expect do @@ -124,8 +150,10 @@ end.to raise_error Mongoid::Errors::Validations, /#{existing_challenger.user_name} can't play./ end end + context 'with another doubles proposed challenge' do let(:challenge) { Fabricate(:challenge, challengers: [Fabricate(:user), Fabricate(:user)], challenged: [Fabricate(:user), Fabricate(:user)]) } + it 'cannot create a duplicate challenge for the challenger' do existing_challenger = challenge.challengers.last expect do @@ -134,23 +162,28 @@ end end end - context '#accept!' do + + describe '#accept!' do let(:challenge) { Fabricate(:challenge) } + it 'can be accepted' do accepted_by = challenge.challenged.first challenge.accept!(accepted_by) expect(challenge.updated_by).to eq accepted_by expect(challenge.state).to eq ChallengeState::ACCEPTED end + it 'requires accepted_by' do challenge.state = ChallengeState::ACCEPTED - expect(challenge).to_not be_valid + expect(challenge).not_to be_valid end + it 'cannot be accepted by another player' do expect do challenge.accept!(challenge.challengers.first) end.to raise_error Mongoid::Errors::Validations, /Only #{challenge.challenged.map(&:user_name).or} can accept this challenge./ end + it 'cannot be accepted twice' do accepted_by = challenge.challenged.first challenge.accept!(accepted_by) @@ -159,23 +192,28 @@ end.to raise_error SlackGamebot::Error, /Challenge has already been accepted./ end end - context '#decline!' do + + describe '#decline!' do let(:challenge) { Fabricate(:challenge) } + it 'can be declined' do declined_by = challenge.challenged.first challenge.decline!(declined_by) expect(challenge.updated_by).to eq declined_by expect(challenge.state).to eq ChallengeState::DECLINED end + it 'requires declined_by' do challenge.state = ChallengeState::DECLINED - expect(challenge).to_not be_valid + expect(challenge).not_to be_valid end + it 'cannot be declined by another player' do expect do challenge.decline!(challenge.challengers.first) end.to raise_error Mongoid::Errors::Validations, /Only #{challenge.challenged.map(&:user_name).or} can decline this challenge./ end + it 'cannot be declined twice' do declined_by = challenge.challenged.first challenge.decline!(declined_by) @@ -184,30 +222,36 @@ end.to raise_error SlackGamebot::Error, /Challenge has already been declined./ end end - context '#cancel!' do + + describe '#cancel!' do let(:challenge) { Fabricate(:challenge) } + it 'can be canceled by challenger' do canceled_by = challenge.challengers.first challenge.cancel!(canceled_by) expect(challenge.updated_by).to eq canceled_by expect(challenge.state).to eq ChallengeState::CANCELED end + it 'can be canceled by challenged' do canceled_by = challenge.challenged.first challenge.cancel!(canceled_by) expect(challenge.updated_by).to eq canceled_by expect(challenge.state).to eq ChallengeState::CANCELED end + it 'requires canceled_by' do challenge.state = ChallengeState::CANCELED - expect(challenge).to_not be_valid + expect(challenge).not_to be_valid end + it 'cannot be canceled_by by another player' do player = Fabricate(:user) expect do challenge.cancel!(player) end.to raise_error Mongoid::Errors::Validations, /Only #{challenge.challengers.map(&:display_name).and} or #{challenge.challenged.map(&:display_name).and} can cancel this challenge./ end + it 'cannot be canceled_by twice' do canceled_by = challenge.challengers.first challenge.cancel!(canceled_by) @@ -216,11 +260,14 @@ end.to raise_error SlackGamebot::Error, /Challenge has already been canceled./ end end - context '#lose!' do + + describe '#lose!' do let(:challenge) { Fabricate(:challenge) } + before do challenge.accept!(challenge.challenged.first) end + it 'can be lost by the challenger' do expect do challenge.lose!(challenge.challengers.first) @@ -232,6 +279,7 @@ expect(game.winners.all? { |player| player.wins == 1 && player.losses == 0 }).to be true expect(game.losers.all? { |player| player.wins == 0 && player.losses == 1 }).to be true end + it 'can be lost by the challenged' do expect do challenge.lose!(challenge.challenged.first) @@ -244,15 +292,18 @@ expect(game.losers.all? { |player| player.wins == 0 && player.losses == 1 }).to be true end end - context '#draw!' do + + describe '#draw!' do let(:challenge) { Fabricate(:challenge) } + before do challenge.accept!(challenge.challenged.first) end + it 'requires both sides to draw' do expect do challenge.draw!(challenge.challengers.first) - end.to_not change(Match, :count) + end.not_to change(Match, :count) expect do challenge.draw!(challenge.challenged.first) end.to change(Match, :count).by(1) @@ -265,9 +316,11 @@ expect(game.losers.all? { |player| player.wins == 0 && player.losses == 0 && player.ties == 1 }).to be true end end + context 'a new challenge' do let(:played_challenge) { Fabricate(:played_challenge) } let(:new_challenge) { Fabricate(:challenge, challengers: played_challenge.challengers, challenged: played_challenge.challenged) } + it 'does not render the played challenge invalid' do expect(new_challenge).to be_valid expect(played_challenge).to be_valid diff --git a/spec/models/elo_spec.rb b/spec/models/elo_spec.rb index 71b05147..17d36c17 100644 --- a/spec/models/elo_spec.rb +++ b/spec/models/elo_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Elo do - context '#team_elo' do + describe '#team_elo' do it 'is rounded average of elo' do expect(Elo.team_elo([User.new(elo: 1)])).to eq 1 expect(Elo.team_elo([User.new(elo: 1), User.new(elo: 2)])).to eq 1.5 diff --git a/spec/models/game_spec.rb b/spec/models/game_spec.rb index 5b864634..fd155027 100644 --- a/spec/models/game_spec.rb +++ b/spec/models/game_spec.rb @@ -1,43 +1,49 @@ require 'spec_helper' describe Game do - context '#find_or_create_from_env!' do + describe '#find_or_create_from_env!' do before do ENV['SLACK_CLIENT_ID'] = 'slack_client_id' ENV['SLACK_CLIENT_SECRET'] = 'slack_client_secret' ENV['SLACK_RUBY_BOT_ALIASES'] = 'pp :pong:' end + + after do + ENV.delete 'SLACK_CLIENT_ID' + ENV.delete 'SLACK_CLIENT_SECRET' + ENV.delete 'SLACK_RUBY_BOT_ALIASES' + end + context 'game' do it 'creates a game' do expect { Game.find_or_create_from_env! }.to change(Game, :count).by(1) game = Game.first - expect(game.name).to be nil + expect(game.name).to be_nil expect(game.client_id).to eq 'slack_client_id' expect(game.client_secret).to eq 'slack_client_secret' expect(game.aliases).to eq(['pp', ':pong:']) end end - after do - ENV.delete 'SLACK_CLIENT_ID' - ENV.delete 'SLACK_CLIENT_SECRET' - ENV.delete 'SLACK_RUBY_BOT_ALIASES' - end end - context '#destroy' do + + describe '#destroy' do let!(:game) { Fabricate(:game) } + it 'can destroy a game' do expect do game.destroy end.to change(Game, :count).by(-1) end + context 'with teams' do let!(:team) { Fabricate(:team, game: game) } + it 'cannot destroy a game that has teams' do expect do expect do game.destroy end.to raise_error 'The game has teams and cannot be destroyed.' - end.to_not change(Game, :count) + end.not_to change(Game, :count) end end end diff --git a/spec/models/match_spec.rb b/spec/models/match_spec.rb index 8722dedd..b83f295e 100644 --- a/spec/models/match_spec.rb +++ b/spec/models/match_spec.rb @@ -1,37 +1,45 @@ require 'spec_helper' describe Match do - context '#to_s' do + describe '#to_s' do let(:match) { Fabricate(:match) } + it 'displays match' do expect(match.to_s).to eq "#{match.winners.first.user_name} defeated #{match.losers.first.user_name} with #{Score.scores_to_string(match.scores)}" end + context 'unregistered users' do before do match.winners.first.unregister! end + it 'removes user name' do expect(match.to_s).to eq " defeated #{match.losers.first.user_name} with #{Score.scores_to_string(match.scores)}" end end + context 'user with nickname' do before do match.winners.first.update_attributes!(nickname: 'bob') end + it 'rewrites user name' do expect(match.to_s).to eq "bob defeated #{match.losers.first.user_name} with #{Score.scores_to_string(match.scores)}" end end end + context 'elo' do context 'singles' do let(:match) { Fabricate(:match) } + it 'updates elo and tau' do expect(match.winners.map(&:elo)).to eq [48] expect(match.winners.map(&:tau)).to eq [0.5] expect(match.losers.map(&:elo)).to eq [-48] expect(match.losers.map(&:tau)).to eq [0.5] end + it 'updates streaks' do expect(match.winners.map(&:winning_streak)).to eq [1] expect(match.winners.map(&:losing_streak)).to eq [0] @@ -39,14 +47,17 @@ expect(match.losers.map(&:losing_streak)).to eq [1] end end + context 'singles tied' do let(:match) { Fabricate(:match, tied: true) } + it 'updates elo and tau' do expect(match.winners.map(&:elo)).to eq [0.0] expect(match.winners.map(&:tau)).to eq [0.5] expect(match.losers.map(&:elo)).to eq [0.0] expect(match.losers.map(&:tau)).to eq [0.5] end + it 'updates streaks' do expect(match.winners.map(&:winning_streak)).to eq [0] expect(match.winners.map(&:losing_streak)).to eq [0] @@ -54,11 +65,14 @@ expect(match.losers.map(&:losing_streak)).to eq [0] end end + context 'two consecutive losses' do let!(:match) { Fabricate(:match) } + before do Fabricate(:match, winners: match.winners, losers: match.losers) end + it 'updates streaks' do expect(match.winners.map(&:winning_streak)).to eq [2] expect(match.winners.map(&:losing_streak)).to eq [0] @@ -66,12 +80,15 @@ expect(match.losers.map(&:losing_streak)).to eq [2] end end + context 'three consecutive losses, then a break for losers preserves losing streak' do let!(:match) { Fabricate(:match) } + before do 2.times { Fabricate(:match, winners: match.winners, losers: match.losers) } Fabricate(:match, winners: match.losers) end + it 'updates streaks' do expect(match.winners.map(&:winning_streak)).to eq [3] expect(match.winners.map(&:losing_streak)).to eq [0] @@ -79,24 +96,30 @@ expect(match.losers.map(&:losing_streak)).to eq [3] end end + context 'after (MAX_TAU * 2) games' do let!(:match) { Fabricate(:match) } + before do (Elo::MAX_TAU * 2).times { Fabricate(:match, winners: match.winners, losers: match.losers) } end + it 'caps tau at MAX_TAU' do expect(match.winners.map(&:tau)).to eq [Elo::MAX_TAU] expect(match.losers.map(&:tau)).to eq [Elo::MAX_TAU] end end + context 'doubles' do let(:match) { Fabricate(:match, challenge: Fabricate(:doubles_challenge)) } + it 'updates elo and tau' do expect(match.winners.map(&:elo)).to eq [48, 48] expect(match.winners.map(&:tau)).to eq [0.5, 0.5] expect(match.losers.map(&:elo)).to eq [-48, -48] expect(match.losers.map(&:tau)).to eq [0.5, 0.5] end + it 'updates streaks' do expect(match.winners.map(&:winning_streak)).to eq [1, 1] expect(match.winners.map(&:losing_streak)).to eq [0, 0] @@ -104,21 +127,25 @@ expect(match.losers.map(&:losing_streak)).to eq [1, 1] end end + context 'two matches against previous losers' do let(:challenge1) { Fabricate(:doubles_challenge) } let(:challengers) { challenge1.challengers } let(:challenged) { [Fabricate(:user), Fabricate(:user)] } let(:match) { Fabricate(:match, challenge: Fabricate(:challenge, challengers: challengers, challenged: challenged)) } + before do challenge1.accept!(challenge1.challenged.first) challenge1.lose!(challenge1.challengers.first) end + it 'updates elo and tau' do expect(match.winners.map(&:elo)).to eq [5, 5] expect(match.winners.map(&:tau)).to eq [1, 1] expect(match.losers.map(&:elo)).to eq [-55, -55] expect(match.losers.map(&:tau)).to eq [0.5, 0.5] end + it 'updates streaks' do expect(match.winners.map(&:winning_streak)).to eq [1, 1] expect(match.winners.map(&:losing_streak)).to eq [1, 1] @@ -126,21 +153,25 @@ expect(match.losers.map(&:losing_streak)).to eq [1, 1] end end + context 'a tie against previous losers' do let(:challenge1) { Fabricate(:doubles_challenge) } let(:challengers) { challenge1.challengers } let(:challenged) { [Fabricate(:user), Fabricate(:user)] } let(:match) { Fabricate(:match, challenge: Fabricate(:challenge, challengers: challengers, challenged: challenged), tied: true) } + before do challenge1.accept!(challenge1.challenged.first) challenge1.lose!(challenge1.challengers.first) end + it 'updates elo and tau' do expect(match.winners.map(&:elo)).to eq [-21, -21] expect(match.winners.map(&:tau)).to eq [1, 1] expect(match.losers.map(&:elo)).to eq [-27, -27] expect(match.losers.map(&:tau)).to eq [0.5, 0.5] end + it 'updates streaks' do expect(match.winners.map(&:winning_streak)).to eq [0, 0] expect(match.winners.map(&:losing_streak)).to eq [1, 1] @@ -148,21 +179,25 @@ expect(match.losers.map(&:losing_streak)).to eq [0, 0] end end + context 'a tie against previous winners' do let(:challenge1) { Fabricate(:doubles_challenge) } let(:challengers) { challenge1.challengers } let(:challenged) { [Fabricate(:user), Fabricate(:user)] } let(:match) { Fabricate(:match, challenge: Fabricate(:challenge, challengers: challengers, challenged: challenged), tied: true) } + before do challenge1.accept!(challenge1.challenged.first) challenge1.lose!(challenge1.challenged.first) end + it 'updates elo and tau' do expect(match.winners.map(&:elo)).to eq [68, 68] expect(match.winners.map(&:tau)).to eq [1, 1] expect(match.losers.map(&:elo)).to eq [-20, -20] expect(match.losers.map(&:tau)).to eq [0.5, 0.5] end + it 'updates streaks' do expect(match.winners.map(&:winning_streak)).to eq [1, 1] expect(match.winners.map(&:losing_streak)).to eq [0, 0] @@ -170,15 +205,18 @@ expect(match.losers.map(&:losing_streak)).to eq [0, 0] end end + context 'two matches against previous winners' do let(:challenge1) { Fabricate(:doubles_challenge) } let(:challengers) { challenge1.challenged } let(:challenged) { [Fabricate(:user), Fabricate(:user)] } let(:match) { Fabricate(:match, challenge: Fabricate(:challenge, challengers: challengers, challenged: challenged)) } + before do challenge1.accept!(challenge1.challenged.first) challenge1.lose!(challenge1.challengers.first) end + it 'updates elo and tau' do expect(match.winners.map(&:elo)).to eq [88, 88] expect(match.winners.map(&:tau)).to eq [1, 1] @@ -186,20 +224,26 @@ expect(match.losers.map(&:tau)).to eq [0.5, 0.5] end end + context 'scores' do let!(:team) { Fabricate(:team) } + it 'loser first' do expect(Match.new(team: team, scores: [[15, 21]])).to be_valid end + it 'loser first with 3 scores' do expect(Match.new(team: team, scores: [[15, 21], [21, 5], [3, 11]])).to be_valid end + it 'winner first' do - expect(Match.new(team: team, scores: [[21, 15]])).to_not be_valid + expect(Match.new(team: team, scores: [[21, 15]])).not_to be_valid end + it 'winner first with 3 scores' do - expect(Match.new(team: team, scores: [[21, 15], [5, 21], [11, 3]])).to_not be_valid + expect(Match.new(team: team, scores: [[21, 15], [5, 21], [11, 3]])).not_to be_valid end + it 'draw' do expect(Match.new(team: team, tied: true, scores: [[15, 15]])).to be_valid end diff --git a/spec/models/score_spec.rb b/spec/models/score_spec.rb index 6b1d6f70..b04b7cc6 100644 --- a/spec/models/score_spec.rb +++ b/spec/models/score_spec.rb @@ -1,64 +1,81 @@ require 'spec_helper' describe Score do - context '#points' do + describe '#points' do it 'returns losing and winning points for one score' do expect(Score.points([[15, 21]])).to eq [15, 21] end + it 'returns losing and winning points for mulitple scores' do expect(Score.points([[15, 21], [11, 9]])).to eq [26, 30] end end - context '#valid?' do + + describe '#valid?' do it 'loser first' do expect(Score.valid?([[15, 21]])).to be true end + it 'loser first with 3 scores' do expect(Score.valid?([[15, 21], [21, 5], [3, 11]])).to be true end + it 'winner first' do expect(Score.valid?([[21, 15]])).to be false end + it 'winner first with 3 scores' do expect(Score.valid?([[21, 15], [5, 21], [11, 3]])).to be false end end - context '#tie?' do + + describe '#tie?' do it 'tie with the same number of points' do expect(Score.tie?([[15, 15]])).to be true end + it 'tie with different number of points' do expect(Score.tie?([[21, 15]])).to be false end + it 'tie with multiple same number of points' do expect(Score.tie?([[15, 14], [14, 15]])).to be true end + it 'tie with multiple different number of points' do expect(Score.tie?([[21, 15], [15, 14]])).to be false end end - context '#parse' do + + describe '#parse' do it 'nil' do - expect(Score.parse(nil)).to be nil + expect(Score.parse(nil)).to be_nil end + it 'x:y' do expect { Score.parse('x:y') }.to raise_error SlackGamebot::Error, 'Invalid score: x:y, invalid value for Integer(): "x".' end + it '-1:5' do expect { Score.parse('-1:5') }.to raise_error SlackGamebot::Error, 'Invalid score: -1:5, points must be greater or equal to zero.' end + it '21:0' do expect(Score.parse('21:0')).to eq [[21, 0]] end + it '5:3' do expect(Score.parse('5:3')).to eq [[5, 3]] end + it '5-3' do expect(Score.parse('5-3')).to eq [[5, 3]] end + it '5:3 9:11' do expect(Score.parse('5:3 9:11')).to eq [[5, 3], [9, 11]] end + it '5:3, 9:11.' do expect(Score.parse('5:3, 9:11.')).to eq [[5, 3], [9, 11]] end diff --git a/spec/models/season_spec.rb b/spec/models/season_spec.rb index 5f726757..6ab94696 100644 --- a/spec/models/season_spec.rb +++ b/spec/models/season_spec.rb @@ -2,89 +2,113 @@ describe Season do let!(:team) { Fabricate(:team) } + context 'with challenges' do let!(:open_challenge) { Fabricate(:challenge) } let!(:matches) { Array.new(3) { Fabricate(:match) } } let!(:season) { Fabricate(:season, team: team) } + it 'archives challenges' do expect(season.challenges.count).to eq 4 end + it 'cancels open challenges' do expect(open_challenge.reload.state).to eq ChallengeState::CANCELED end + it 'resets users' do - expect(User.all.detect { |u| u.wins != 0 || u.losses != 0 }).to be nil + expect(User.all.detect { |u| u.wins != 0 || u.losses != 0 }).to be_nil end + it 'saves user ranks' do expect(season.user_ranks.count).to eq 6 end + it 'to_s' do expect(season.to_s).to eq "#{season.created_at.strftime('%F')}: #{season.winners.map(&:to_s).and}, 3 matches, 6 players" end end + context 'without challenges' do let!(:team) { Fabricate(:team) } let(:season) { Season.new(team: team) } + it 'cannot be created' do - expect(season).to_not be_valid + expect(season).not_to be_valid expect(season.errors.messages).to eq(challenges: ['No matches have been recorded.']) end + it 'to_s' do expect(season.to_s).to eq 'Current: n/a, 0 matches, 0 players' end end + context 'without challenges and a lost match' do let!(:team) { Fabricate(:team) } let(:challenger) { Fabricate(:user, team: team) } let(:challenged) { Fabricate(:user, team: team) } let(:season) { Season.new(team: team) } + before do - ::Match.lose!(team: team, winners: [challenger], losers: [challenged]) + Match.lose!(team: team, winners: [challenger], losers: [challenged]) end + it 'can be created' do expect(season).to be_valid end + it 'to_s' do expect(season.to_s).to eq "Current: #{season.winners.map(&:to_s).and}, 1 match, 2 players" end + it 'has one winner' do expect(season.winners.count).to eq 1 end end + context 'current season with one match' do let!(:match) { Fabricate(:match) } let(:season) { Season.new(team: team) } + it 'to_s' do expect(season.to_s).to eq "Current: #{season.winners.map(&:to_s).and}, 1 match, 2 players" end + it 'has one winner' do expect(season.winners.count).to eq 1 end end + context 'current season with multiple matches and one winner' do let(:user) { Fabricate(:user, team: team) } let!(:matches) { Array.new(3) { Fabricate(:match, challenge: Fabricate(:challenge, challengers: [user])) } } let(:season) { Season.new(team: team) } + it 'to_s' do expect(season.to_s).to eq "Current: #{season.winners.map(&:to_s).and}, 3 matches, 4 players" end + it 'has one winner' do expect(season.winners.count).to eq 1 expect(season.winners.first.wins).to eq 3 end + context 'with an unplayed challenge' do before do Fabricate(:challenge) end + it 'only counts played matches' do expect(season.to_s).to eq "Current: #{season.winners.map(&:to_s).and}, 3 matches, 4 players" end end end + context 'current season with two winners' do let!(:matches) { Array.new(2) { Fabricate(:match, team: team) } } let!(:matches) { Array.new(2) { Fabricate(:match, team: team) } } let(:season) { Season.new(team: team) } + it 'has two winners' do expect(season.winners.count).to eq 2 expect(season.winners.map(&:wins).uniq).to eq([1]) diff --git a/spec/models/team_spec.rb b/spec/models/team_spec.rb index 5deb8492..11d2e3ff 100644 --- a/spec/models/team_spec.rb +++ b/spec/models/team_spec.rb @@ -2,10 +2,16 @@ describe Team do let!(:game) { Fabricate(:game) } - context '#find_or_create_from_env!' do + + describe '#find_or_create_from_env!' do before do ENV['SLACK_API_TOKEN'] = 'token' end + + after do + ENV.delete 'SLACK_API_TOKEN' + end + context 'team', vcr: { cassette_name: 'team_info' } do it 'creates a team' do expect { Team.find_or_create_from_env! }.to change(Team, :count).by(1) @@ -17,14 +23,13 @@ expect(team.game).to eq game end end - after do - ENV.delete 'SLACK_API_TOKEN' - end end - context '#destroy' do + + describe '#destroy' do let!(:team) { Fabricate(:team) } let!(:match) { Fabricate(:match, team: team) } let!(:season) { Fabricate(:season, team: team) } + it 'destroys dependent records' do expect(Team.count).to eq 1 expect(User.count).to eq 2 @@ -44,12 +49,14 @@ end.to change(Season, :count).by(-1) end end - context '#purge!' do + + describe '#purge!' do let!(:active_team) { Fabricate(:team) } let!(:inactive_team) { Fabricate(:team, active: false) } let!(:inactive_team_a_week_ago) { Fabricate(:team, updated_at: 1.week.ago, active: false) } let!(:inactive_team_two_weeks_ago) { Fabricate(:team, updated_at: 2.weeks.ago, active: false) } let!(:inactive_team_a_month_ago) { Fabricate(:team, updated_at: 1.month.ago, active: false) } + it 'destroys teams inactive for two weeks' do expect do Team.purge! @@ -57,117 +64,150 @@ expect(Team.find(active_team.id)).to eq active_team expect(Team.find(inactive_team.id)).to eq inactive_team expect(Team.find(inactive_team_a_week_ago.id)).to eq inactive_team_a_week_ago - expect(Team.find(inactive_team_two_weeks_ago.id)).to be nil - expect(Team.find(inactive_team_a_month_ago.id)).to be nil + expect(Team.find(inactive_team_two_weeks_ago.id)).to be_nil + expect(Team.find(inactive_team_a_month_ago.id)).to be_nil end end - context '#dead? and #asleep?' do + + describe '#dead? and #asleep?' do context 'default' do let(:team) { Fabricate(:team) } + it 'false' do expect(team.asleep?).to be false expect(team.dead?).to be false end end + context 'team created three weeks ago' do let(:team) { Fabricate(:team, created_at: 3.weeks.ago) } + it 'dead=false' do expect(team.asleep?).to be true expect(team.dead?).to be false end + context 'with a recent challenge' do let!(:challenge) { Fabricate(:challenge, team: team) } + it 'false' do expect(team.asleep?).to be false expect(team.dead?).to be false end end + context 'with a recent match' do let!(:match) { Fabricate(:match, team: team) } + it 'false' do expect(team.asleep?).to be false expect(team.dead?).to be false end end + context 'with a recent match lost to' do let!(:match) { Fabricate(:match_lost_to, team: team) } + it 'false' do expect(team.asleep?).to be false expect(team.dead?).to be false end end + context 'with an old challenge' do let!(:challenge) { Fabricate(:challenge, updated_at: 3.weeks.ago, team: team) } + it 'true' do expect(team.asleep?).to be true expect(team.dead?).to be false end end end + context 'team created over a month ago' do let(:team) { Fabricate(:team, created_at: 32.days.ago) } + it 'dead=true' do expect(team.dead?).to be true end + context 'with a recent challenge' do let!(:challenge) { Fabricate(:challenge, updated_at: 2.weeks.ago, team: team) } + it 'true' do expect(team.dead?).to be false end end + context 'with an old challenge' do let!(:challenge) { Fabricate(:challenge, updated_at: 5.weeks.ago, team: team) } + it 'true' do expect(team.dead?).to be true end end end end + context 'gifs' do let!(:team) { Fabricate(:team) } + context 'with a played challenge' do let(:challenge) { Fabricate(:played_challenge) } + context 'with a new challenge' do let!(:open_challenge) { Fabricate(:challenge, challengers: challenge.challengers, challenged: challenge.challenged) } + it 'can be set' do - expect(team.challenges.detect { |c| !c.valid? }).to be nil - expect { team.update_attributes!(gifs: !team.gifs) }.to_not raise_error + expect(team.challenges.detect { |c| !c.valid? }).to be_nil + expect { team.update_attributes!(gifs: !team.gifs) }.not_to raise_error end end end end + context 'subscribed states' do let(:today) { DateTime.parse('2018/7/15 12:42pm') } let(:subscribed_team) { Fabricate(:team, subscribed: true) } let(:team_created_today) { Fabricate(:team, created_at: today) } let(:team_created_1_week_ago) { Fabricate(:team, created_at: (today - 1.week)) } let(:team_created_3_weeks_ago) { Fabricate(:team, created_at: (today - 3.weeks)) } + before do Timecop.travel(today + 1.day) end + + after do + Timecop.return + end + it 'subscription_expired?' do expect(subscribed_team.subscription_expired?).to be false expect(team_created_1_week_ago.subscription_expired?).to be false expect(team_created_3_weeks_ago.subscription_expired?).to be true end + it 'trial_ends_at' do expect { subscribed_team.trial_ends_at }.to raise_error 'Team is subscribed.' expect(team_created_today.trial_ends_at).to eq team_created_today.created_at + 2.weeks expect(team_created_1_week_ago.trial_ends_at).to eq team_created_1_week_ago.created_at + 2.weeks expect(team_created_3_weeks_ago.trial_ends_at).to eq team_created_3_weeks_ago.created_at + 2.weeks end + it 'remaining_trial_days' do expect { subscribed_team.remaining_trial_days }.to raise_error 'Team is subscribed.' expect(team_created_today.remaining_trial_days).to eq 13 expect(team_created_1_week_ago.remaining_trial_days).to eq 6 expect(team_created_3_weeks_ago.remaining_trial_days).to eq 0 end - context '#inform_trial!' do + + describe '#inform_trial!' do it 'subscribed' do - expect(subscribed_team).to_not receive(:inform!) - expect(subscribed_team).to_not receive(:inform_admin!) + expect(subscribed_team).not_to receive(:inform!) + expect(subscribed_team).not_to receive(:inform_admin!) subscribed_team.inform_trial! end + it '1 week ago' do expect(team_created_1_week_ago).to receive(:inform!).with( "Your trial subscription expires in 6 days. #{team_created_1_week_ago.subscribe_text}" @@ -177,32 +217,35 @@ ) team_created_1_week_ago.inform_trial! end + it 'expired' do - expect(team_created_3_weeks_ago).to_not receive(:inform!) - expect(team_created_3_weeks_ago).to_not receive(:inform_admin!) + expect(team_created_3_weeks_ago).not_to receive(:inform!) + expect(team_created_3_weeks_ago).not_to receive(:inform_admin!) team_created_3_weeks_ago.inform_trial! end + it 'informs once' do expect(team_created_1_week_ago).to receive(:inform!).once expect(team_created_1_week_ago).to receive(:inform_admin!).once 2.times { team_created_1_week_ago.inform_trial! } end end - after do - Timecop.return - end end - context '#inform!' do + + describe '#inform!' do let(:team) { Fabricate(:team) } + before do team.bot_user_id = 'bot_user_id' end + it 'sends message to all channels', vcr: { cassette_name: 'users_conversations' } do expect_any_instance_of(Slack::Web::Client).to receive(:chat_postMessage).exactly(25).times.and_return('ts' => '1503435956.000247') team.inform!(message: 'message') end end - context '#activated' do + + describe '#activated' do pending 'DMs installing user when activated' end end diff --git a/spec/models/user_rank_spec.rb b/spec/models/user_rank_spec.rb index 413e8205..ed8c1bac 100644 --- a/spec/models/user_rank_spec.rb +++ b/spec/models/user_rank_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe UserRank do - context '#from_user' do + describe '#from_user' do it 'creates a record' do user = Fabricate(:user) user_rank = UserRank.from_user(user) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 709e2191..9642ec62 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2,27 +2,34 @@ describe User do let(:team) { Fabricate(:team) } - context '#find_by_slack_mention!' do + + describe '#find_by_slack_mention!' do let(:web_client) { double(Slack::Web::Client, users_info: nil) } let(:client) { double(Slack::RealTime::Client, owner: team, web_client: web_client) } let!(:user) { Fabricate(:user, team: team, nickname: 'bob') } + it 'finds by slack id' do expect(User.find_by_slack_mention!(client, "<@#{user.user_id}>")).to eq user end + it 'finds by username' do expect(User.find_by_slack_mention!(client, user.user_name)).to eq user end + it 'finds by username is case-insensitive' do expect(User.find_by_slack_mention!(client, user.user_name.capitalize)).to eq user end + it 'requires a known user' do expect do User.find_by_slack_mention!(client, '<@nobody>') end.to raise_error SlackGamebot::Error, "I don't know who <@nobody> is! Ask them to _register_." end + it 'finds by nickname' do expect(User.find_by_slack_mention!(client, user.nickname)).to eq user end + %w[here channel].each do |name| it "always finds #{name}" do mention = "" @@ -34,105 +41,129 @@ end end end + context 'with a mismatching user id in slack_mention' do let!(:user) { Fabricate(:user, team: team, nickname: 'bob') } let(:web_client) { double(Slack::Web::Client, users_info: { user: { id: user.user_id, name: user.user_name } }) } let(:client) { double(Slack::RealTime::Client, owner: team, web_client: web_client) } + it 'finds by different slack id returned from slack info' do expect(User.find_by_slack_mention!(client, '<@unknown>')).to eq user end end - context '#find_many_by_slack_mention!' do + + describe '#find_many_by_slack_mention!' do let(:web_client) { double(Slack::Web::Client, users_info: nil) } let(:client) { double(Slack::RealTime::Client, owner: team, web_client: web_client) } let!(:users) { [Fabricate(:user, team: team), Fabricate(:user, team: team)] } + it 'finds by slack_id or slack_mention' do results = User.find_many_by_slack_mention!(client, [users.first.user_name, users.last.slack_mention]) - expect(results).to contain_exactly(*users) + expect(results).to match_array(users) end + it 'requires known users' do expect do User.find_many_by_slack_mention!(client, %w[foo bar]) end.to raise_error SlackGamebot::Error, "I don't know who foo is! Ask them to _register_." end end - context '#find_create_or_update_by_slack_id!', vcr: { cassette_name: 'user_info' } do + + describe '#find_create_or_update_by_slack_id!', vcr: { cassette_name: 'user_info' } do let(:client) { SlackRubyBot::Client.new } + before do client.owner = team end + context 'without a user' do it 'creates a user' do expect do user = User.find_create_or_update_by_slack_id!(client, 'U42') - expect(user).to_not be_nil + expect(user).not_to be_nil expect(user.user_id).to eq 'U42' expect(user.user_name).to eq 'username' end.to change(User, :count).by(1) end end + context 'with a user' do let!(:user) { Fabricate(:user, team: team) } + it 'creates another user' do expect do User.find_create_or_update_by_slack_id!(client, 'U42') end.to change(User, :count).by(1) end + it 'updates the username of the existing user' do expect do User.find_create_or_update_by_slack_id!(client, user.user_id) - end.to_not change(User, :count) + end.not_to change(User, :count) expect(user.reload.user_name).to eq 'username' end end end - context '#to_s' do + + describe '#to_s' do let(:user) { Fabricate(:user, elo: 48, team: Fabricate(:team, elo: 2)) } + it 'respects team elo' do expect(user.to_s).to include 'elo: 50' end + context 'unregistered user' do before do user.update_attributes!(registered: false) end + it 'hides name' do expect(user.to_s).to eq ': 0 wins, 0 losses (elo: 50)' end end + context 'user with nickname' do before do user.update_attributes!(nickname: 'bob') end + it 'rewrites user name' do expect(user.to_s).to eq 'bob: 0 wins, 0 losses (elo: 50)' end end + context 'with a longest winning streak >= 3' do before do user.update_attributes!(winning_streak: 3) end + it 'displays lws' do expect(user.to_s).to eq "#{user.user_name}: 0 wins, 0 losses (elo: 50, lws: 3)" end end + context 'equal streaks' do before do user.update_attributes!(winning_streak: 5, losing_streak: 5) end + it 'prefers winning streak' do expect(user.to_s).to eq "#{user.user_name}: 0 wins, 0 losses (elo: 50, lws: 5)" end end + context 'with a longest losing streak >= 3' do before do user.update_attributes!(losing_streak: 3, winning_streak: 2) end + it 'displays lls' do expect(user.to_s).to eq "#{user.user_name}: 0 wins, 0 losses (elo: 50, lls: 3)" end end end - context '#reset_all' do + + describe '#reset_all' do it 'resets all user stats' do user1 = Fabricate(:user, elo: 48, losses: 1, wins: 2, ties: 3, tau: 0.5) user2 = Fabricate(:user, elo: 54, losses: 2, wins: 1, tau: 1.5) @@ -144,7 +175,7 @@ expect(user1.ties).to eq 0 expect(user1.tau).to eq 0 expect(user1.elo).to eq 0 - expect(user1.rank).to be nil + expect(user1.rank).to be_nil expect(user1.winning_streak).to eq 0 expect(user1.losing_streak).to eq 0 expect(user1.elo_history).to eq [] @@ -153,19 +184,21 @@ expect(user2.ties).to eq 0 expect(user2.tau).to eq 0 expect(user2.elo).to eq 0 - expect(user2.rank).to be nil + expect(user2.rank).to be_nil expect(user2.winning_streak).to eq 0 expect(user2.losing_streak).to eq 0 expect(user2.elo_history).to eq [] end end - context '#rank!' do + + describe '#rank!' do it 'updates when elo changes' do user = Fabricate(:user) - expect(user.rank).to be nil + expect(user.rank).to be_nil user.update_attributes!(elo: 65, wins: 1) expect(user.rank).to eq 1 end + it 'stores elo history' do user = Fabricate(:user) user.update_attributes!(elo: 65, wins: 1) @@ -173,6 +206,7 @@ user.update_attributes!(elo: 45, wins: 2) expect(user.elo_history).to eq [0, 65, 45] end + it 'ranks four players' do user1 = Fabricate(:user, elo: 100, wins: 4, losses: 0) user2 = Fabricate(:user, elo: 40, wins: 1, losses: 1) @@ -183,24 +217,28 @@ expect(user3.reload.rank).to eq 3 expect(user4.reload.rank).to eq 2 end + it 'ranks players with the same elo and different wins' do user1 = Fabricate(:user, elo: 40, wins: 1, losses: 0) user2 = Fabricate(:user, elo: 40, wins: 4, losses: 0) expect(user1.reload.rank).to eq 2 expect(user2.reload.rank).to eq 1 end + it 'ranks players with the same elo, wins and losses and different ties' do user1 = Fabricate(:user, elo: 40, wins: 1, losses: 0, ties: 0) user2 = Fabricate(:user, elo: 40, wins: 1, losses: 0, ties: 1) expect(user1.reload.rank).to eq 2 expect(user2.reload.rank).to eq 1 end + it 'ranks players with the same elo and wins/losses equally' do user1 = Fabricate(:user, elo: 1, wins: 1, losses: 1) user2 = Fabricate(:user, elo: 2, wins: 1, losses: 1) expect(user1.rank).to eq 1 expect(user1.rank).to eq user2.rank end + it 'is updated for all users' do user1 = Fabricate(:user, elo: 65, wins: 1) expect(user1.rank).to eq 1 @@ -211,25 +249,30 @@ expect(user1.rank).to eq 1 expect(user2.reload.rank).to eq 2 end + it 'does not rank unregistered users' do user1 = Fabricate(:user, elo: 40, wins: 1, losses: 0, registered: false) user2 = Fabricate(:user, elo: 40, wins: 4, losses: 0) - expect(user1.reload.rank).to be nil + expect(user1.reload.rank).to be_nil expect(user2.reload.rank).to eq 1 end end - context '.ranked' do + + describe '.ranked' do it 'returns an empty list' do expect(User.ranked).to eq [] end + it 'ignores players without rank' do user1 = Fabricate(:user, elo: 1, wins: 1, losses: 1) Fabricate(:user) expect(User.ranked).to eq [user1] end end - context '.rank_section' do + + describe '.rank_section' do let(:team) { Fabricate(:team) } + it 'returns a section' do user1 = Fabricate(:user, team: team, elo: 100, wins: 4, losses: 0) user2 = Fabricate(:user, team: team, elo: 40, wins: 1, losses: 1) @@ -240,10 +283,12 @@ expect(User.rank_section(team, [user1, user3])).to eq [user1, user4, user3] expect(User.rank_section(team, [user1, user3, user4])).to eq [user1, user4, user3] end + it 'limits by team' do user = Fabricate(:user, elo: 100, wins: 4, losses: 0) expect(User.rank_section(Fabricate(:team), [user])).to eq [] end + it 'only returns one unranked user' do user1 = Fabricate(:user, team: team) user2 = Fabricate(:user, team: team) @@ -251,12 +296,15 @@ expect(User.rank_section(team, [user1, user2])).to eq [user1, user2] end end - context '#calculate_streaks!' do + + describe '#calculate_streaks!' do let(:user) { Fabricate(:user) } + it 'is 0 by default' do expect(user.winning_streak).to eq 0 expect(user.losing_streak).to eq 0 end + it 'is 0 without matches' do user.calculate_streaks! expect(user.winning_streak).to eq 0 diff --git a/spec/slack-gamebot/app_spec.rb b/spec/slack-gamebot/app_spec.rb index 83c2f6af..7677ab1a 100644 --- a/spec/slack-gamebot/app_spec.rb +++ b/spec/slack-gamebot/app_spec.rb @@ -4,30 +4,34 @@ subject do SlackGamebot::App.instance end - context '#instance' do + + describe '#instance' do it 'is an instance of the market app' do - expect(subject).to be_a_kind_of(SlackRubyBotServer::App) + expect(subject).to be_a(SlackRubyBotServer::App) expect(subject).to be_an_instance_of(SlackGamebot::App) end end + context 'teams' do let!(:active_team) { Fabricate(:team, created_at: Time.now.utc) } let!(:active_team_one_week_ago) { Fabricate(:team, created_at: 1.week.ago) } let!(:active_team_four_weeks_ago) { Fabricate(:team, created_at: 5.weeks.ago) } let!(:subscribed_team_a_month_ago) { Fabricate(:team, created_at: 1.month.ago, subscribed: true) } let(:teams) { [active_team, active_team_one_week_ago, active_team_four_weeks_ago, subscribed_team_a_month_ago] } + before do allow(Team).to receive(:active).and_return(teams) end - context '#deactivate_dead_teams!' do + + describe '#deactivate_dead_teams!' do it 'deactivates teams inactive for two weeks' do - expect(active_team).to_not receive(:inform!) - expect(active_team).to_not receive(:inform_admin!) - expect(active_team_one_week_ago).to_not receive(:inform!) - expect(active_team_one_week_ago).to_not receive(:inform_admin!) + expect(active_team).not_to receive(:inform!) + expect(active_team).not_to receive(:inform_admin!) + expect(active_team_one_week_ago).not_to receive(:inform!) + expect(active_team_one_week_ago).not_to receive(:inform_admin!) expect(active_team_four_weeks_ago).to receive(:deactivate!).and_call_original - expect(subscribed_team_a_month_ago).to_not receive(:inform!) - expect(subscribed_team_a_month_ago).to_not receive(:inform_admin!) + expect(subscribed_team_a_month_ago).not_to receive(:inform!) + expect(subscribed_team_a_month_ago).not_to receive(:inform_admin!) subject.send(:deactivate_dead_teams!) expect(active_team.reload.active).to be true expect(active_team_one_week_ago.reload.active).to be true @@ -39,23 +43,27 @@ end end end + context 'subscribed' do - include_context :stripe_mock + include_context 'stripe mock' let(:plan) { stripe_helper.create_plan(id: 'slack-playplay-yearly', amount: 2999, name: 'Plan') } let(:customer) { Stripe::Customer.create(source: stripe_helper.generate_card_token, plan: plan.id, email: 'foo@bar.com') } let!(:team) { Fabricate(:team, subscribed: true, stripe_customer_id: customer.id) } - context '#check_subscribed_teams!' do + + describe '#check_subscribed_teams!' do it 'ignores active subscriptions' do - expect_any_instance_of(Team).to_not receive(:inform!) - expect_any_instance_of(Team).to_not receive(:inform_admin!) + expect_any_instance_of(Team).not_to receive(:inform!) + expect_any_instance_of(Team).not_to receive(:inform_admin!) subject.send(:check_subscribed_teams!) end + it 'notifies past due subscription' do customer.subscriptions.data.first['status'] = 'past_due' expect(Stripe::Customer).to receive(:retrieve).and_return(customer) expect_any_instance_of(Team).to receive(:inform_admin!).with("Your subscription to Plan ($29.99) is past due. #{team.update_cc_text}") subject.send(:check_subscribed_teams!) end + it 'notifies canceled subscription' do customer.subscriptions.data.first['status'] = 'canceled' expect(Stripe::Customer).to receive(:retrieve).and_return(customer) @@ -63,6 +71,7 @@ subject.send(:check_subscribed_teams!) expect(team.reload.subscribed?).to be false end + it 'notifies no active subscriptions' do customer.subscriptions.data = [] expect(Stripe::Customer).to receive(:retrieve).and_return(customer) diff --git a/spec/slack-gamebot/commands/accept_spec.rb b/spec/slack-gamebot/commands/accept_spec.rb index 2ef8e640..4963d214 100644 --- a/spec/slack-gamebot/commands/accept_spec.rb +++ b/spec/slack-gamebot/commands/accept_spec.rb @@ -4,9 +4,11 @@ let!(:team) { Fabricate(:team) } let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + context 'regular challenge' do let(:challenged) { Fabricate(:user, team: team, user_name: 'username') } let!(:challenge) { Fabricate(:challenge, team: team, challenged: [challenged]) } + it 'accepts a challenge' do expect(message: "#{SlackRubyBot.config.user} accept", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "#{challenge.challenged.map(&:display_name).and} accepted #{challenge.challengers.map(&:display_name).and}'s challenge." @@ -14,11 +16,13 @@ expect(challenge.reload.state).to eq ChallengeState::ACCEPTED end end + context 'open challenge' do let(:user) { Fabricate(:user, team: team) } let(:acceptor) { Fabricate(:user, team: team) } let(:anyone_challenged) { Fabricate(:user, team: team, user_id: User::ANYONE) } let!(:challenge) { Fabricate(:challenge, team: team, challengers: [user], challenged: [anyone_challenged]) } + it 'accepts an open challenge' do allow_any_instance_of(Slack::Web::Client).to receive(:users_info).and_return(nil) expect(message: "#{SlackRubyBot.config.user} accept", user: acceptor.user_id, channel: challenge.channel).to respond_with_slack_message( @@ -28,6 +32,7 @@ expect(challenge.state).to eq ChallengeState::ACCEPTED expect(challenge.challenged).to eq [acceptor] end + it 'cannot accept an open challenge with themselves' do allow_any_instance_of(Slack::Web::Client).to receive(:users_info).and_return(nil) expect(message: "#{SlackRubyBot.config.user} accept", user: user.user_id, channel: challenge.channel).to respond_with_slack_message( diff --git a/spec/slack-gamebot/commands/cancel_spec.rb b/spec/slack-gamebot/commands/cancel_spec.rb index 9467cb4b..118dd9f4 100644 --- a/spec/slack-gamebot/commands/cancel_spec.rb +++ b/spec/slack-gamebot/commands/cancel_spec.rb @@ -4,9 +4,11 @@ let!(:team) { Fabricate(:team) } let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + context 'challenger' do let(:challenger) { Fabricate(:user, user_name: 'username') } let!(:challenge) { Fabricate(:challenge, challengers: [challenger]) } + it 'cancels a challenge' do expect(message: "#{SlackRubyBot.config.user} cancel", user: challenger.user_id, channel: challenge.channel).to respond_with_slack_message( "#{challenge.challengers.map(&:display_name).and} canceled a challenge against #{challenge.challenged.map(&:display_name).and}." @@ -14,9 +16,11 @@ expect(challenge.reload.state).to eq ChallengeState::CANCELED end end + context 'challenged' do let(:challenged) { Fabricate(:user, user_name: 'username') } let!(:challenge) { Fabricate(:challenge, challenged: [challenged]) } + it 'cancels a challenge' do expect(message: "#{SlackRubyBot.config.user} cancel", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "#{challenge.challenged.map(&:display_name).and} canceled a challenge against #{challenge.challengers.map(&:display_name).and}." diff --git a/spec/slack-gamebot/commands/challenge_question_spec.rb b/spec/slack-gamebot/commands/challenge_question_spec.rb index 92b14611..4f3e0b74 100644 --- a/spec/slack-gamebot/commands/challenge_question_spec.rb +++ b/spec/slack-gamebot/commands/challenge_question_spec.rb @@ -6,13 +6,15 @@ let(:client) { app.send(:client) } let(:user) { Fabricate(:user, user_name: 'username') } let(:opponent) { Fabricate(:user) } + it 'displays elo at stake for a singles challenge' do expect do expect(message: "#{SlackRubyBot.config.user} challenge? <@#{opponent.user_id}>", user: user.user_id, channel: 'pongbot').to respond_with_slack_message( "#{user.slack_mention} challenging #{opponent.slack_mention} to a match is worth 48 elo." ) - end.to_not change(Challenge, :count) + end.not_to change(Challenge, :count) end + it 'displays elo at stake for a doubles challenge' do opponent2 = Fabricate(:user, team: team) teammate = Fabricate(:user, team: team) @@ -20,12 +22,14 @@ expect(message: "#{SlackRubyBot.config.user} challenge? #{opponent.slack_mention} #{opponent2.user_name} with #{teammate.user_name}", user: user.user_id, channel: 'pongbot').to respond_with_slack_message( "#{user.slack_mention} and #{teammate.slack_mention} challenging #{opponent.slack_mention} and #{opponent2.slack_mention} to a match is worth 48 elo." ) - end.to_not change(Challenge, :count) + end.not_to change(Challenge, :count) end + context 'with unbalanced option enabled' do before do team.update_attributes!(unbalanced: true) end + it 'displays elo at stake with different number of opponents' do opponent1 = Fabricate(:user) opponent2 = Fabricate(:user) @@ -33,13 +37,15 @@ expect(message: "#{SlackRubyBot.config.user} challenge? #{opponent1.slack_mention} #{opponent2.slack_mention}", user: user.user_id, channel: 'pongbot').to respond_with_slack_message( "#{user.slack_mention} challenging #{opponent1.slack_mention} and #{opponent2.slack_mention} to a match is worth 24 and 48 elo." ) - end.to_not change(Challenge, :count) + end.not_to change(Challenge, :count) end end + context 'subscription expiration' do before do team.update_attributes!(created_at: 3.weeks.ago) end + it 'prevents new challenge questions' do expect(message: "#{SlackRubyBot.config.user} challenge? <@#{opponent.user_id}>", user: user.user_id, channel: 'pongbot').to respond_with_slack_message( "Your trial subscription has expired. Subscribe your team for $29.99 a year at https://www.playplay.io/subscribe?team_id=#{team.team_id}&game=#{team.game.name}." diff --git a/spec/slack-gamebot/commands/challenge_spec.rb b/spec/slack-gamebot/commands/challenge_spec.rb index 28c54eb6..5ab46cec 100644 --- a/spec/slack-gamebot/commands/challenge_spec.rb +++ b/spec/slack-gamebot/commands/challenge_spec.rb @@ -6,6 +6,7 @@ let(:client) { app.send(:client) } let(:user) { Fabricate(:user, user_name: 'username') } let(:opponent) { Fabricate(:user) } + it 'creates a singles challenge by user id' do expect do expect(message: "#{SlackRubyBot.config.user} challenge <@#{opponent.user_id}>", user: user.user_id, channel: 'pongbot').to respond_with_slack_message( @@ -18,6 +19,7 @@ expect(challenge.challengers).to eq [user] expect(challenge.challenged).to eq [opponent] end + it 'creates a singles challenge by user name' do expect do expect(message: "#{SlackRubyBot.config.user} challenge #{opponent.slack_mention}", user: user.user_id, channel: 'pongbot').to respond_with_slack_message( @@ -25,6 +27,7 @@ ) end.to change(Challenge, :count).by(1) end + it 'creates a doubles challenge by user name' do opponent2 = Fabricate(:user, team: team) teammate = Fabricate(:user, team: team) @@ -39,6 +42,7 @@ expect(challenge.challengers).to eq [teammate, user] expect(challenge.challenged).to eq [opponent2, opponent] end + it 'creates a singles challenge by user name case-insensitive' do expect do expect(message: "#{SlackRubyBot.config.user} challenge #{opponent.user_name.capitalize}", user: user.user_id, channel: 'pongbot').to respond_with_slack_message( @@ -46,13 +50,15 @@ ) end.to change(Challenge, :count).by(1) end + it 'requires an opponent' do expect do expect(message: "#{SlackRubyBot.config.user} challenge", user: user.user_id, channel: 'pongbot').to respond_with_slack_message( 'Number of teammates (1) and opponents (0) must match.' ) - end.to_not change(Challenge, :count) + end.not_to change(Challenge, :count) end + it 'requires the same number of opponents' do opponent1 = Fabricate(:user) opponent2 = Fabricate(:user) @@ -60,12 +66,14 @@ expect(message: "#{SlackRubyBot.config.user} challenge #{opponent1.slack_mention} #{opponent2.slack_mention}", user: user.user_id, channel: 'pongbot').to respond_with_slack_message( 'Number of teammates (1) and opponents (2) must match.' ) - end.to_not change(Challenge, :count) + end.not_to change(Challenge, :count) end + context 'with unbalanced option enabled' do before do team.update_attributes!(unbalanced: true) end + it 'allows different number of opponents' do opponent1 = Fabricate(:user) opponent2 = Fabricate(:user) @@ -79,37 +87,44 @@ expect(challenge.challenged).to eq [opponent1, opponent2] end end + it 'does not butcher names with special characters' do allow(client.web_client).to receive(:users_info) expect(message: "#{SlackRubyBot.config.user} challenge Jung-hwa", user: user.user_id, channel: 'pongbot').to respond_with_slack_message( "I don't know who Jung-hwa is! Ask them to _register_." ) end + context 'requires the opponent to be registered' do before do opponent.unregister! end + it 'by slack id' do expect(message: "#{SlackRubyBot.config.user} challenge #{opponent.slack_mention}", user: user.user_id, channel: 'pongbot').to respond_with_slack_message( "I don't know who #{opponent.slack_mention} is! Ask them to _register_." ) end + it 'requires the opponent to be registered by name' do expect(message: "#{SlackRubyBot.config.user} challenge #{opponent.user_name}", user: user.user_id, channel: 'pongbot').to respond_with_slack_message( "I don't know who #{opponent.user_name} is! Ask them to _register_." ) end end + context 'subscription expiration' do before do team.update_attributes!(created_at: 3.weeks.ago) end + it 'prevents new challenges' do expect(message: "#{SlackRubyBot.config.user} challenge <@#{opponent.user_id}>", user: user.user_id, channel: 'pongbot').to respond_with_slack_message( "Your trial subscription has expired. Subscribe your team for $29.99 a year at https://www.playplay.io/subscribe?team_id=#{team.team_id}&game=#{team.game.name}." ) end end + User::EVERYONE.each do |username| it "challenges #{username}" do expect do diff --git a/spec/slack-gamebot/commands/challenges_spec.rb b/spec/slack-gamebot/commands/challenges_spec.rb index 4fd3250d..d060df09 100644 --- a/spec/slack-gamebot/commands/challenges_spec.rb +++ b/spec/slack-gamebot/commands/challenges_spec.rb @@ -5,12 +5,14 @@ let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } let(:user) { Fabricate(:user, user_name: 'username') } + context 'with challenges' do let!(:challenge_proposed) { Fabricate(:challenge) } let!(:challenge_canceled) { Fabricate(:canceled_challenge) } let!(:challenge_declined) { Fabricate(:declined_challenge) } let!(:challenge_accepted) { Fabricate(:accepted_challenge) } let!(:challenge_played) { Fabricate(:played_challenge) } + it 'displays a proposed and accepted challenges' do expect(message: "#{SlackRubyBot.config.user} challenges", user: user.user_id, channel: challenge_proposed.channel).to respond_with_slack_message( "a challenge between #{challenge_proposed.challengers.map(&:display_name).and} and #{challenge_proposed.challenged.map(&:display_name).and} was proposed just now\n" \ @@ -18,6 +20,7 @@ ) end end + context 'without challenges' do it 'displays all challenges have been played' do expect(message: "#{SlackRubyBot.config.user} challenges", user: user.user_id, channel: 'channel').to respond_with_slack_message( diff --git a/spec/slack-gamebot/commands/decline_spec.rb b/spec/slack-gamebot/commands/decline_spec.rb index 440b1330..060b4c18 100644 --- a/spec/slack-gamebot/commands/decline_spec.rb +++ b/spec/slack-gamebot/commands/decline_spec.rb @@ -6,6 +6,7 @@ let(:client) { app.send(:client) } let(:challenged) { Fabricate(:user, user_name: 'username') } let!(:challenge) { Fabricate(:challenge, challenged: [challenged]) } + it 'declines a challenge' do expect(message: "#{SlackRubyBot.config.user} decline", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "#{challenge.challenged.map(&:display_name).and} declined #{challenge.challengers.map(&:display_name).and} challenge." diff --git a/spec/slack-gamebot/commands/default_spec.rb b/spec/slack-gamebot/commands/default_spec.rb index b6fb9558..55804174 100644 --- a/spec/slack-gamebot/commands/default_spec.rb +++ b/spec/slack-gamebot/commands/default_spec.rb @@ -5,11 +5,13 @@ let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } let(:message_hook) { SlackRubyBot::Hooks::Message.new } + it 'default' do expect(client).to receive(:say).with(channel: 'channel', text: SlackGamebot::INFO) expect(client).to receive(:say).with(channel: 'channel', gif: 'robot') message_hook.call(client, Hashie::Mash.new(channel: 'channel', text: SlackRubyBot.config.user)) end + it 'upcase' do expect(client).to receive(:say).with(channel: 'channel', text: SlackGamebot::INFO) expect(client).to receive(:say).with(channel: 'channel', gif: 'robot') diff --git a/spec/slack-gamebot/commands/demote_spec.rb b/spec/slack-gamebot/commands/demote_spec.rb index 6439280e..1cfdc81b 100644 --- a/spec/slack-gamebot/commands/demote_spec.rb +++ b/spec/slack-gamebot/commands/demote_spec.rb @@ -4,8 +4,10 @@ let!(:team) { Fabricate(:team) } let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + context 'captain' do let(:user) { Fabricate(:user, team: team, user_name: 'username', captain: true) } + it 'demotes self' do another_user = Fabricate(:user, team: team, captain: true) expect(message: "#{SlackRubyBot.config.user} demote me", user: user.user_id).to respond_with_slack_message( @@ -13,11 +15,13 @@ ) expect(another_user.reload.captain?).to be true end + it 'cannot demote the last captain' do expect(message: "#{SlackRubyBot.config.user} demote me", user: user.user_id).to respond_with_slack_message( "You cannot demote yourself, you're the last captain. Promote someone else first." ) end + it 'cannot demote another captain' do another_user = Fabricate(:user, team: team, captain: true) expect(message: "#{SlackRubyBot.config.user} demote #{another_user.user_name}", user: user.user_id).to respond_with_slack_message( @@ -26,9 +30,11 @@ expect(another_user.reload.captain?).to be true end end + context 'not captain' do let!(:captain) { Fabricate(:user, team: team, captain: true) } let(:user) { Fabricate(:user, team: team, user_name: 'username') } + it 'cannot demote' do expect(message: "#{SlackRubyBot.config.user} demote me", user: user.user_id).to respond_with_slack_message( "You're not a captain, sorry." diff --git a/spec/slack-gamebot/commands/draw_spec.rb b/spec/slack-gamebot/commands/draw_spec.rb index 9ad4681a..3e8314ac 100644 --- a/spec/slack-gamebot/commands/draw_spec.rb +++ b/spec/slack-gamebot/commands/draw_spec.rb @@ -4,12 +4,15 @@ let!(:team) { Fabricate(:team) } let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + context 'with a challenge' do let(:challenged) { Fabricate(:user, user_name: 'username') } let!(:challenge) { Fabricate(:challenge, challenged: [challenged]) } + before do challenge.accept!(challenged) end + it 'draw' do expect(message: "#{SlackRubyBot.config.user} draw", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "Match is a draw, waiting to hear from #{challenge.challengers.map(&:display_name).and}." @@ -18,6 +21,7 @@ expect(challenge.state).to eq ChallengeState::DRAWN expect(challenge.draw).to eq challenge.challenged end + it 'draw with a score' do expect(message: "#{SlackRubyBot.config.user} draw 2:2", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "Match is a draw, waiting to hear from #{challenge.challengers.map(&:display_name).and}. Recorded the score of 2:2." @@ -28,10 +32,12 @@ expect(challenge.draw_scores?).to be true expect(challenge.draw_scores).to eq [[2, 2]] end + context 'confirmation' do before do challenge.draw!(challenge.challengers.first) end + it 'confirmed' do expect(message: "#{SlackRubyBot.config.user} draw", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "Match has been recorded! #{challenge.challengers.map(&:display_name).and} tied with #{challenge.challenged.map(&:display_name).and}." @@ -40,6 +46,7 @@ expect(challenge.state).to eq ChallengeState::PLAYED expect(challenge.draw).to eq challenge.challenged + challenge.challengers end + it 'with score' do expect(message: "#{SlackRubyBot.config.user} draw 3:3", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "Match has been recorded! #{challenge.challengers.map(&:display_name).and} tied with #{challenge.challenged.map(&:display_name).and} with the score of 3:3." @@ -47,11 +54,13 @@ challenge.reload expect(challenge.match.scores).to eq [[3, 3]] end + it 'with invalid score' do expect(message: "#{SlackRubyBot.config.user} draw 21:15", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( 'In a tie both sides must score the same number of points.' ) end + it 'draw with scores' do expect(message: "#{SlackRubyBot.config.user} draw 21:15 15:21", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "Match has been recorded! #{challenge.challengers.map(&:display_name).and} tied with #{challenge.challenged.map(&:display_name).and} with the scores of 15:21 21:15." @@ -60,18 +69,21 @@ expect(challenge.match.scores).to eq [[21, 15], [15, 21]] end end + it 'draw already confirmed' do challenge.draw!(challenge.challenged.first) expect(message: "#{SlackRubyBot.config.user} draw", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "Match is a draw, still waiting to hear from #{challenge.challengers.map(&:display_name).and}." ) end + it 'does not update a previously lost match' do challenge.lose!(challenge.challenged.first) expect(message: "#{SlackRubyBot.config.user} draw", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( 'No challenge to draw!' ) end + it 'does not update a previously won match' do challenge.lose!(challenge.challengers.first) expect(message: "#{SlackRubyBot.config.user} draw", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( @@ -79,9 +91,11 @@ ) end end + context 'without a challenge' do let(:winner) { Fabricate(:user) } let(:loser) { Fabricate(:user, user_name: 'username') } + it 'draw to' do expect do expect do @@ -89,11 +103,12 @@ "Match is a draw, waiting to hear from #{winner.user_name}." ) end.to change(Challenge, :count).by(1) - end.to_not change(Match, :count) + end.not_to change(Match, :count) challenge = Challenge.desc(:_id).first expect(challenge.state).to eq ChallengeState::DRAWN expect(challenge.draw).to eq [loser] end + it 'draw with a score' do expect do expect do @@ -101,17 +116,19 @@ "Match is a draw, waiting to hear from #{winner.user_name}. Recorded the score of 2:2." ) end.to change(Challenge, :count).by(1) - end.to_not change(Match, :count) + end.not_to change(Match, :count) challenge = Challenge.desc(:_id).first expect(challenge.state).to eq ChallengeState::DRAWN expect(challenge.draw).to eq [loser] expect(challenge.draw_scores?).to be true expect(challenge.draw_scores).to eq [[2, 2]] end + context 'confirmation' do before do allow_any_instance_of(Slack::Web::Client).to receive(:users_info).and_return(nil) end + let!(:challenge) do Challenge.create!( team: loser.team, channel: 'channel', @@ -121,11 +138,13 @@ state: ChallengeState::DRAWN ) end + it 'still waiting' do expect(message: "#{SlackRubyBot.config.user} draw", user: loser.user_id, channel: 'channel').to respond_with_slack_message( "Match is a draw, still waiting to hear from #{winner.user_name}." ) end + it 'confirmed' do expect(message: "#{SlackRubyBot.config.user} draw", user: winner.user_id, channel: 'channel').to respond_with_slack_message( "Match has been recorded! #{loser.user_name} tied with #{winner.user_name}." @@ -134,6 +153,7 @@ expect(challenge.state).to eq ChallengeState::PLAYED expect(challenge.draw).to eq [loser, winner] end + it 'with score' do expect(message: "#{SlackRubyBot.config.user} draw 3:3", user: winner.user_id, channel: 'channel').to respond_with_slack_message( "Match has been recorded! #{loser.user_name} tied with #{winner.user_name} with the score of 3:3." @@ -141,11 +161,13 @@ challenge.reload expect(challenge.match.scores).to eq [[3, 3]] end + it 'with invalid score' do expect(message: "#{SlackRubyBot.config.user} draw 21:15", user: winner.user_id, channel: 'channel').to respond_with_slack_message( 'In a tie both sides must score the same number of points.' ) end + it 'draw with scores' do expect(message: "#{SlackRubyBot.config.user} draw 21:15 15:21", user: winner.user_id, channel: challenge.channel).to respond_with_slack_message( "Match has been recorded! #{loser.user_name} tied with #{winner.user_name} with the scores of 15:21 21:15." diff --git a/spec/slack-gamebot/commands/help_spec.rb b/spec/slack-gamebot/commands/help_spec.rb index e5d27997..25813dc5 100644 --- a/spec/slack-gamebot/commands/help_spec.rb +++ b/spec/slack-gamebot/commands/help_spec.rb @@ -4,16 +4,20 @@ let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } let(:message_hook) { SlackRubyBot::Hooks::Message.new } + context 'subscribed team' do let!(:team) { Fabricate(:team, subscribed: true) } + it 'help' do expect(client).to receive(:say).with(channel: 'channel', text: [SlackGamebot::Commands::Help::HELP].join("\n")) expect(client).to receive(:say).with(channel: 'channel', gif: 'help') message_hook.call(client, Hashie::Mash.new(channel: 'channel', text: "#{SlackRubyBot.config.user} help")) end end + context 'non-subscribed team' do let!(:team) { Fabricate(:team) } + it 'help' do expect(client).to receive(:say).with(channel: 'channel', text: [SlackGamebot::Commands::Help::HELP, team.trial_message].join("\n")) expect(client).to receive(:say).with(channel: 'channel', gif: 'help') diff --git a/spec/slack-gamebot/commands/hi_spec.rb b/spec/slack-gamebot/commands/hi_spec.rb index 02ae56e2..2ce18e33 100644 --- a/spec/slack-gamebot/commands/hi_spec.rb +++ b/spec/slack-gamebot/commands/hi_spec.rb @@ -4,6 +4,7 @@ let!(:team) { Fabricate(:team) } let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + it 'says hi' do expect(message: "#{SlackRubyBot.config.user} hi").to respond_with_slack_message('Hi <@user>!') end diff --git a/spec/slack-gamebot/commands/info_spec.rb b/spec/slack-gamebot/commands/info_spec.rb index dedbc7ea..099b7c50 100644 --- a/spec/slack-gamebot/commands/info_spec.rb +++ b/spec/slack-gamebot/commands/info_spec.rb @@ -5,6 +5,7 @@ let(:client) { app.send(:client) } let(:message_hook) { SlackRubyBot::Hooks::Message.new } let(:team) { Fabricate(:team) } + it 'info' do expect(client).to receive(:say).with(channel: 'channel', text: SlackGamebot::INFO) message_hook.call(client, Hashie::Mash.new(channel: 'channel', text: "#{SlackRubyBot.config.user} info")) diff --git a/spec/slack-gamebot/commands/leaderboard_spec.rb b/spec/slack-gamebot/commands/leaderboard_spec.rb index a758ffc3..d4e1af0f 100644 --- a/spec/slack-gamebot/commands/leaderboard_spec.rb +++ b/spec/slack-gamebot/commands/leaderboard_spec.rb @@ -4,50 +4,61 @@ let!(:team) { Fabricate(:team) } let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + shared_examples_for 'leaderboard' do context 'with players' do let!(:user_elo_42) { Fabricate(:user, elo: 42, wins: 3, losses: 2) } let!(:user_elo_48) { Fabricate(:user, elo: 48, wins: 2, losses: 3) } + it 'displays leaderboard sorted by elo' do expect(message: "#{SlackRubyBot.config.user} leaderboard").to respond_with_slack_message "1. #{user_elo_48}\n2. #{user_elo_42}" end + it 'excludes unregistered users' do user_elo_48.unregister! expect(message: "#{SlackRubyBot.config.user} leaderboard").to respond_with_slack_message "1. #{user_elo_42}" end + it 'limits to max' do expect(message: "#{SlackRubyBot.config.user} leaderboard 1").to respond_with_slack_message "1. #{user_elo_48}" end + it 'limits to team leaderboard max' do team.update_attributes!(leaderboard_max: 1) expect(message: "#{SlackRubyBot.config.user} leaderboard").to respond_with_slack_message "1. #{user_elo_48}" end + it 'supports infinity' do user_elo_43 = Fabricate(:user, elo: 43, wins: 2, losses: 3) user_elo_44 = Fabricate(:user, elo: 44, wins: 2, losses: 3) expect(message: "#{SlackRubyBot.config.user} leaderboard infinity").to respond_with_slack_message "1. #{user_elo_48}\n2. #{user_elo_44}\n3. #{user_elo_43}\n4. #{user_elo_42}" end + it 'supports -infinity' do user_elo_43 = Fabricate(:user, elo: 43, wins: 2, losses: 3) user_elo_44 = Fabricate(:user, elo: 44, wins: 2, losses: 3) expect(message: "#{SlackRubyBot.config.user} leaderboard -infinity").to respond_with_slack_message "1. #{user_elo_42}\n2. #{user_elo_43}\n3. #{user_elo_44}\n4. #{user_elo_48}" end + it 'supports -number' do user_elo_43 = Fabricate(:user, elo: 43, wins: 2, losses: 3) expect(message: "#{SlackRubyBot.config.user} leaderboard -2").to respond_with_slack_message "1. #{user_elo_42}\n2. #{user_elo_43}" end end + context 'without players' do it 'says no players' do expect(message: "#{SlackRubyBot.config.user} leaderboard").to respond_with_slack_message "There're no ranked players." end end end + it_behaves_like 'leaderboard' context 'with another team' do let!(:team2) { Fabricate(:team) } let!(:user2_elo_42) { Fabricate(:user, team: team2, elo: 42, wins: 3, losses: 2) } let!(:user2_elo_48) { Fabricate(:user, team: team2, elo: 48, wins: 2, losses: 3) } + it_behaves_like 'leaderboard' end end diff --git a/spec/slack-gamebot/commands/lost_spec.rb b/spec/slack-gamebot/commands/lost_spec.rb index e8f84b80..29b83a80 100644 --- a/spec/slack-gamebot/commands/lost_spec.rb +++ b/spec/slack-gamebot/commands/lost_spec.rb @@ -4,12 +4,15 @@ let!(:team) { Fabricate(:team) } let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + context 'with an existing challenge' do let(:challenged) { Fabricate(:user, user_name: 'username') } let!(:challenge) { Fabricate(:challenge, challenged: [challenged]) } + before do challenge.accept!(challenged) end + it 'lost' do expect(message: "#{SlackRubyBot.config.user} lost", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "Match has been recorded! #{challenge.challengers.map(&:display_name).and} defeated #{challenge.challenged.map(&:display_name).and}." @@ -25,6 +28,7 @@ expect(loser.elo).to eq(-48) expect(loser.tau).to eq 0.5 end + it 'updates existing challenge when lost to' do expect(message: "#{SlackRubyBot.config.user} lost to #{challenge.challengers.first.user_name}", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "Match has been recorded! #{challenge.challengers.map(&:display_name).and} defeated #{challenge.challenged.map(&:display_name).and}." @@ -34,6 +38,7 @@ expect(challenge.match.winners).to eq challenge.challengers expect(challenge.match.losers).to eq challenge.challenged end + it 'lost with score' do expect(message: "#{SlackRubyBot.config.user} lost 15:21", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "Match has been recorded! #{challenge.challengers.map(&:display_name).and} defeated #{challenge.challenged.map(&:display_name).and} with the score of 21:15." @@ -42,11 +47,13 @@ expect(challenge.match.scores).to eq [[15, 21]] expect(challenge.match.resigned?).to be false end + it 'lost with invalid score' do expect(message: "#{SlackRubyBot.config.user} lost 21:15", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( 'Loser scores must come first.' ) end + it 'lost with scores' do expect(message: "#{SlackRubyBot.config.user} lost 21:15 14:21 5:11", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "Match has been recorded! #{challenge.challengers.map(&:display_name).and} defeated #{challenge.challenged.map(&:display_name).and} with the scores of 15:21 21:14 11:5." @@ -54,16 +61,19 @@ challenge.reload expect(challenge.match.scores).to eq [[21, 15], [14, 21], [5, 11]] end + it 'lost with a crushing score' do expect(message: "#{SlackRubyBot.config.user} lost 5:21", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "Match has been recorded! #{challenge.challengers.map(&:display_name).and} crushed #{challenge.challenged.map(&:display_name).and} with the score of 21:5." ) end + it 'lost in a close game' do expect(message: "#{SlackRubyBot.config.user} lost 19:21", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "Match has been recorded! #{challenge.challengers.map(&:display_name).and} narrowly defeated #{challenge.challenged.map(&:display_name).and} with the score of 21:19." ) end + it 'lost amending scores' do challenge.lose!(challenged) expect(message: "#{SlackRubyBot.config.user} lost 21:15 14:21 5:11", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( @@ -72,6 +82,7 @@ challenge.reload expect(challenge.match.scores).to eq [[21, 15], [14, 21], [5, 11]] end + it 'does not update a previously lost match' do challenge.lose!(challenged, [[11, 21]]) challenge2 = Fabricate(:challenge, challenged: [challenged]) @@ -83,8 +94,9 @@ expect(challenge.match.scores).to eq [[11, 21]] challenge2.reload expect(challenge2.state).to eq ChallengeState::PLAYED - expect(challenge2.match.scores).to be nil + expect(challenge2.match.scores).to be_nil end + it 'does not update a previously won match' do challenge.lose!(challenge.challengers.first, [[11, 21]]) expect(message: "#{SlackRubyBot.config.user} lost", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( @@ -92,14 +104,17 @@ ) end end + context 'with an existing unbalanced challenge' do let(:challenged1) { Fabricate(:user, user_name: 'username') } let(:challenged2) { Fabricate(:user) } let(:challenge) { Fabricate(:challenge, challenged: [challenged1, challenged2]) } + before do team.update_attributes!(unbalanced: true) challenge.accept!(challenged1) end + it 'lost' do expect(message: "#{SlackRubyBot.config.user} lost", user: challenged1.user_id, channel: challenge.channel).to respond_with_slack_message( "Match has been recorded! #{challenge.challengers.map(&:display_name).and} defeated #{challenge.challenged.map(&:display_name).and}." @@ -116,30 +131,34 @@ expect(loser.tau).to eq 0.5 end end + context 'lost to' do let(:loser) { Fabricate(:user, user_name: 'username') } let(:winner) { Fabricate(:user) } + it 'a player' do expect do expect do expect(message: "#{SlackRubyBot.config.user} lost to #{winner.user_name}", user: loser.user_id, channel: 'channel').to respond_with_slack_message( "Match has been recorded! #{winner.user_name} defeated #{loser.user_name}." ) - end.to_not change(Challenge, :count) + end.not_to change(Challenge, :count) end.to change(Match, :count).by(1) match = Match.asc(:_id).last expect(match.winners).to eq [winner] expect(match.losers).to eq [loser] end + it 'same player' do expect do expect do expect(message: "#{SlackRubyBot.config.user} lost to #{loser.user_name}", user: loser.user_id, channel: 'channel').to respond_with_slack_message( 'You cannot lose to yourself!' ) - end.to_not change(Challenge, :count) - end.to_not change(Match, :count) + end.not_to change(Challenge, :count) + end.not_to change(Match, :count) end + it 'two players' do winner2 = Fabricate(:user, team: team) loser2 = Fabricate(:user, team: team) @@ -148,12 +167,13 @@ expect(message: "#{SlackRubyBot.config.user} lost to #{winner.user_name} #{winner2.user_name} with #{loser2.user_name}", user: loser.user_id, channel: 'pongbot').to respond_with_slack_message( "Match has been recorded! #{winner.user_name} and #{winner2.user_name} defeated #{loser.user_name} and #{loser2.user_name}." ) - end.to_not change(Challenge, :count) + end.not_to change(Challenge, :count) end.to change(Match, :count).by(1) match = Match.asc(:_id).last expect(match.winners).to eq [winner2, winner] expect(match.losers).to eq [loser2, loser] end + it 'two players with scores' do winner2 = Fabricate(:user, team: team) loser2 = Fabricate(:user, team: team) @@ -162,20 +182,21 @@ expect(message: "#{SlackRubyBot.config.user} lost to #{winner.user_name} #{winner2.user_name} with #{loser2.user_name} 15:21", user: loser.user_id, channel: 'pongbot').to respond_with_slack_message( "Match has been recorded! #{winner.user_name} and #{winner2.user_name} defeated #{loser.user_name} and #{loser2.user_name} with the score of 21:15." ) - end.to_not change(Challenge, :count) + end.not_to change(Challenge, :count) end.to change(Match, :count).by(1) match = Match.asc(:_id).last expect(match.winners).to eq [winner2, winner] expect(match.losers).to eq [loser2, loser] expect(match.scores).to eq [[15, 21]] end + it 'with score' do expect do expect do expect(message: "#{SlackRubyBot.config.user} lost to #{winner.user_name} 15:21", user: loser.user_id, channel: 'channel').to respond_with_slack_message( "Match has been recorded! #{winner.user_name} defeated #{loser.user_name} with the score of 21:15." ) - end.to_not change(Challenge, :count) + end.not_to change(Challenge, :count) end.to change(Match, :count).by(1) match = Match.asc(:_id).last expect(match.winners).to eq [winner] @@ -183,13 +204,14 @@ expect(match.scores).to eq [[15, 21]] expect(match.resigned?).to be false end + it 'with scores' do expect do expect do expect(message: "#{SlackRubyBot.config.user} lost to #{winner.user_name} 21:15 14:21 5:11", user: loser.user_id, channel: 'channel').to respond_with_slack_message( "Match has been recorded! #{winner.user_name} defeated #{loser.user_name} with the scores of 15:21 21:14 11:5." ) - end.to_not change(Challenge, :count) + end.not_to change(Challenge, :count) end.to change(Match, :count).by(1) match = Match.asc(:_id).last expect(match.winners).to eq [winner] diff --git a/spec/slack-gamebot/commands/matches_spec.rb b/spec/slack-gamebot/commands/matches_spec.rb index 5fb1a27a..02d0ec38 100644 --- a/spec/slack-gamebot/commands/matches_spec.rb +++ b/spec/slack-gamebot/commands/matches_spec.rb @@ -4,6 +4,7 @@ let!(:team) { Fabricate(:team) } let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + shared_examples_for 'matches' do let!(:team) { Fabricate(:team) } let(:user) { Fabricate(:user, user_name: 'username') } @@ -14,6 +15,7 @@ let!(:match1) { Fabricate(:match, challenge: doubles_challenge) } let!(:match2) { Fabricate(:match, challenge: doubles_challenge) } let!(:match3) { Fabricate(:match, challenge: doubles_challenge) } + it 'displays top 10 matches' do expect_any_instance_of(Array).to receive(:take).with(10).and_call_original expect(message: "#{SlackRubyBot.config.user} matches", user: user.user_id, channel: match1.challenge.channel).to respond_with_slack_message([ @@ -21,16 +23,19 @@ "#{match0} once" ].join("\n")) end + it 'limits number of matches' do expect(message: "#{SlackRubyBot.config.user} matches 1", user: user.user_id, channel: match1.challenge.channel).to respond_with_slack_message([ "#{match1} 3 times" ].join("\n")) end + it 'displays only matches for requested users' do expect(message: "#{SlackRubyBot.config.user} matches #{match1.challenge.challenged.first.user_name}", user: user.user_id, channel: match1.challenge.channel).to respond_with_slack_message( "#{match1} 3 times" ) end + it 'displays only matches for requested users with a limit' do another_challenge = Fabricate(:challenge, challengers: [match1.challenge.challenged.first]) Fabricate(:match, challenge: another_challenge) @@ -39,41 +44,50 @@ ) end end + context 'with a doubles match' do let!(:match) { Fabricate(:match, challenge: doubles_challenge) } + it 'displays user matches' do expect(message: "#{SlackRubyBot.config.user} matches", user: user.user_id, channel: match.challenge.channel).to respond_with_slack_message( "#{match} once" ) end end + context 'with a singles match' do let!(:match) { Fabricate(:match, challenge: singles_challenge) } + it 'displays user matches' do expect(message: "#{SlackRubyBot.config.user} matches", user: user.user_id, channel: match.challenge.channel).to respond_with_slack_message( "#{match} once" ) end end + context 'without matches' do it 'displays' do expect(message: "#{SlackRubyBot.config.user} matches", user: user.user_id, channel: 'channel').to respond_with_slack_message('No matches.') end end + context 'matches in prior seasons' do let!(:match1) { Fabricate(:match, challenge: singles_challenge) } let!(:season) { Fabricate(:season) } let(:singles_challenge2) { Fabricate(:challenge, challengers: [user]) } let!(:match2) { Fabricate(:match, challenge: singles_challenge2) } + it 'displays user matches in current season only' do expect(message: "#{SlackRubyBot.config.user} matches", user: user.user_id, channel: match2.challenge.channel).to respond_with_slack_message( "#{match2} once" ) end end + context 'lost to' do let(:loser) { Fabricate(:user, user_name: 'username') } let(:winner) { Fabricate(:user) } + it 'a player' do expect(message: "#{SlackRubyBot.config.user} lost to #{winner.user_name}", user: loser.user_id, channel: 'channel').to respond_with_slack_message( "Match has been recorded! #{winner.user_name} defeated #{loser.user_name}." @@ -84,10 +98,12 @@ end end end + it_behaves_like 'matches' context 'with another team' do let!(:team2) { Fabricate(:team) } let!(:team2_match) { Fabricate(:match, team: team2) } + it_behaves_like 'matches' end end diff --git a/spec/slack-gamebot/commands/promote_spec.rb b/spec/slack-gamebot/commands/promote_spec.rb index 08f0f572..509955e0 100644 --- a/spec/slack-gamebot/commands/promote_spec.rb +++ b/spec/slack-gamebot/commands/promote_spec.rb @@ -5,11 +5,13 @@ let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } let(:user) { Fabricate(:user, team: team, user_name: 'username', captain: true) } + it 'gives help' do expect(message: "#{SlackRubyBot.config.user} promote", user: user.user_id).to respond_with_slack_message( 'Try _promote @someone_.' ) end + it 'promotes another user' do another_user = Fabricate(:user, team: team) expect(message: "#{SlackRubyBot.config.user} promote #{another_user.user_name}", user: user.user_id).to respond_with_slack_message( @@ -17,12 +19,14 @@ ) expect(another_user.reload.captain?).to be true end + it 'cannot promote self' do expect(message: "#{SlackRubyBot.config.user} promote username", user: user.user_id).to respond_with_slack_message( "#{user.user_name} is already a captain." ) expect(user.reload.captain?).to be true end + it 'promotes multiple users' do another_user1 = Fabricate(:user, team: team) another_user2 = Fabricate(:user, team: team) @@ -34,6 +38,7 @@ expect(another_user2.reload.captain?).to be true expect(another_user3.reload.captain?).to be true end + it 'cannot promote another captain' do another_user = Fabricate(:user, team: team, captain: true) expect(message: "#{SlackRubyBot.config.user} promote #{another_user.user_name}", user: user.user_id).to respond_with_slack_message( @@ -41,6 +46,7 @@ ) expect(another_user.reload.captain?).to be true end + it 'cannot promote when not a captain' do user.demote! another_user = Fabricate(:user, team: team, captain: true) diff --git a/spec/slack-gamebot/commands/rank_spec.rb b/spec/slack-gamebot/commands/rank_spec.rb index e5995918..bccb8f28 100644 --- a/spec/slack-gamebot/commands/rank_spec.rb +++ b/spec/slack-gamebot/commands/rank_spec.rb @@ -4,6 +4,7 @@ let!(:team) { Fabricate(:team) } let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + shared_examples_for 'rank' do let!(:user_elo_12) { Fabricate(:user, elo: 12, wins: 0, losses: 25) } let!(:user_elo_38) { Fabricate(:user, elo: 38, wins: 3, losses: 3) } @@ -13,36 +14,44 @@ it 'ranks the requester if no argument is passed' do expect(message: "#{SlackRubyBot.config.user} rank", user: user_elo_42.user_id).to respond_with_slack_message '3. username: 3 wins, 2 losses (elo: 42)' end + it 'ranks someone who is not ranked' do user = Fabricate(:user, team: team) expect(message: "#{SlackRubyBot.config.user} rank", user: user.user_id).to respond_with_slack_message 'username: not ranked' end + it 'ranks someone who is not ranked by default' do user1 = Fabricate(:user, team: team) Fabricate(:user, team: team) expect(message: "#{SlackRubyBot.config.user} rank", user: user1.user_id).to respond_with_slack_message 'username: not ranked' end + it 'ranks someone who is not ranked by name' do user1 = Fabricate(:user, team: team) Fabricate(:user, team: team) expect(message: "#{SlackRubyBot.config.user} rank #{user1.user_name}", user: user1.user_id).to respond_with_slack_message "#{user1.user_name}: not ranked" end + it 'ranks one player by slack mention' do expect(message: "#{SlackRubyBot.config.user} rank #{user_elo_42.slack_mention}").to respond_with_slack_message "3. #{user_elo_42}" end + it 'ranks one player by user_id' do expect(message: "#{SlackRubyBot.config.user} rank <@#{user_elo_42.user_id}>").to respond_with_slack_message "3. #{user_elo_42}" end + it 'shows the smallest range of ranks for a list of players' do users = [user_elo_38, user_elo_67].map(&:slack_mention) expect(message: "#{SlackRubyBot.config.user} rank #{users.join(' ')}").to respond_with_slack_message "2. #{user_elo_67}\n3. #{user_elo_42}\n4. #{user_elo_38}" end end + it_behaves_like 'rank' context 'with another team' do let!(:team2) { Fabricate(:team) } let!(:user2_elo_42) { Fabricate(:user, team: team2, elo: 42, wins: 3, losses: 2) } let!(:user2_elo_48) { Fabricate(:user, team: team2, elo: 48, wins: 2, losses: 3) } + it_behaves_like 'rank' end end diff --git a/spec/slack-gamebot/commands/register_spec.rb b/spec/slack-gamebot/commands/register_spec.rb index a8148fa5..a125328f 100644 --- a/spec/slack-gamebot/commands/register_spec.rb +++ b/spec/slack-gamebot/commands/register_spec.rb @@ -4,35 +4,40 @@ let!(:team) { Fabricate(:team) } let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + it 'registers a new user and promotes them to captain' do Fabricate(:user, team: Fabricate(:team)) # another user in another team expect do expect(message: "#{SlackRubyBot.config.user} register", user: 'user').to respond_with_slack_message("Welcome <@user>! You're ready to play. You're also team captain.") end.to change(User, :count).by(1) end + it 'registers a new user' do Fabricate(:user, team: team, captain: true) expect do expect(message: "#{SlackRubyBot.config.user} register", user: 'user').to respond_with_slack_message("Welcome <@user>! You're ready to play.") end.to change(User, :count).by(1) end + it 'renames an existing user' do Fabricate(:user, user_id: 'user') expect do expect(message: "#{SlackRubyBot.config.user} register", user: 'user').to respond_with_slack_message("Welcome back <@user>, I've updated your registration. You're also team captain.") - end.to_not change(User, :count) + end.not_to change(User, :count) end + it 'already registered' do Fabricate(:user, user_id: 'user', user_name: 'username', captain: true) expect do expect(message: "#{SlackRubyBot.config.user} register", user: 'user').to respond_with_slack_message("Welcome back <@user>, you're already registered. You're also team captain.") - end.to_not change(User, :count) + end.not_to change(User, :count) end + it 'registeres a previously unregistered existing user' do user = Fabricate(:user, user_id: 'user', registered: false) expect do expect(message: "#{SlackRubyBot.config.user} register", user: 'user').to respond_with_slack_message("Welcome back <@user>, I've updated your registration. You're also team captain.") - end.to_not change(User, :count) + end.not_to change(User, :count) expect(user.reload.registered?).to be true end end diff --git a/spec/slack-gamebot/commands/reset_spec.rb b/spec/slack-gamebot/commands/reset_spec.rb index becbc19e..2774f9a7 100644 --- a/spec/slack-gamebot/commands/reset_spec.rb +++ b/spec/slack-gamebot/commands/reset_spec.rb @@ -4,36 +4,43 @@ let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } let!(:team) { Fabricate(:team) } + it 'requires a captain' do Fabricate(:user, captain: true, team: team) Fabricate(:user, user_name: 'username') - expect(::User).to_not receive(:reset_all!).with(team) + expect(User).not_to receive(:reset_all!).with(team) expect(message: "#{SlackRubyBot.config.user} reset").to respond_with_slack_message("You're not a captain, sorry.") end + it 'requires a team name' do - expect(::User).to_not receive(:reset_all!).with(team) + expect(User).not_to receive(:reset_all!).with(team) expect(message: "#{SlackRubyBot.config.user} reset").to respond_with_slack_message("Missing team name or id, confirm with _reset #{team.name}_ or _reset #{team.team_id}_.") end + it 'requires a matching team name' do - expect(::User).to_not receive(:reset_all!).with(team) + expect(User).not_to receive(:reset_all!).with(team) expect(message: "#{SlackRubyBot.config.user} reset invalid").to respond_with_slack_message("Invalid team name or id, confirm with _reset #{team.name}_ or _reset #{team.team_id}_.") end + it 'resets with the correct team name' do Fabricate(:match) - expect(::User).to receive(:reset_all!).with(team).once + expect(User).to receive(:reset_all!).with(team).once expect(message: "#{SlackRubyBot.config.user} reset #{team.name}").to respond_with_slack_message('Welcome to the new season!') end + it 'resets with the correct team id' do Fabricate(:match) - expect(::User).to receive(:reset_all!).with(team).once + expect(User).to receive(:reset_all!).with(team).once expect(message: "#{SlackRubyBot.config.user} reset #{team.team_id}").to respond_with_slack_message('Welcome to the new season!') end + it 'resets a team that has a period and space in the name' do team.update_attributes!(name: 'Pets.com Delivery') Fabricate(:match) - expect(::User).to receive(:reset_all!).with(team).once + expect(User).to receive(:reset_all!).with(team).once expect(message: "#{SlackRubyBot.config.user} reset #{team.name}").to respond_with_slack_message('Welcome to the new season!') end + it 'cancels open challenges' do proposed_challenge = Fabricate(:challenge, state: ChallengeState::PROPOSED) @@ -45,6 +52,7 @@ expect(proposed_challenge.reload.state).to eq ChallengeState::CANCELED expect(accepted_challenge.reload.state).to eq ChallengeState::CANCELED end + it 'resets user stats' do Fabricate(:match) user = Fabricate(:user, elo: 48, losses: 1, wins: 2, tau: 0.5) @@ -55,6 +63,7 @@ expect(user.tau).to eq 0 expect(user.elo).to eq 0 end + it 'resets user stats for the right team' do Fabricate(:match) user1 = Fabricate(:user, elo: 48, losses: 1, wins: 2, tau: 0.5, ties: 3) @@ -75,11 +84,13 @@ expect(user2.elo).to eq 48 expect(user2.ties).to eq 3 end + it 'cannot be reset unless any games have been played' do expect(message: "#{SlackRubyBot.config.user} reset #{team.name}").to respond_with_slack_message('No matches have been recorded.') end + it 'can be reset with a match lost' do - ::Match.lose!(team: team, winners: [Fabricate(:user, team: team)], losers: [Fabricate(:user, team: team)]) + Match.lose!(team: team, winners: [Fabricate(:user, team: team)], losers: [Fabricate(:user, team: team)]) expect(message: "#{SlackRubyBot.config.user} reset #{team.name}").to respond_with_slack_message('Welcome to the new season!') end end diff --git a/spec/slack-gamebot/commands/resigned_spec.rb b/spec/slack-gamebot/commands/resigned_spec.rb index e2cd84fa..5e54add2 100644 --- a/spec/slack-gamebot/commands/resigned_spec.rb +++ b/spec/slack-gamebot/commands/resigned_spec.rb @@ -4,12 +4,15 @@ let!(:team) { Fabricate(:team) } let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + context 'with a challenge' do let(:challenged) { Fabricate(:user, user_name: 'username') } let!(:challenge) { Fabricate(:challenge, challenged: [challenged]) } + before do challenge.accept!(challenged) end + it 'resigned' do expect(message: "#{SlackRubyBot.config.user} resigned", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( "Match has been recorded! #{challenge.challenged.map(&:display_name).and} resigned against #{challenge.challengers.map(&:display_name).and}." @@ -20,28 +23,32 @@ expect(challenge.match.losers).to eq challenge.challenged expect(challenge.match.resigned?).to be true end + it 'resigned with score' do expect(message: "#{SlackRubyBot.config.user} resigned 15:21", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message( 'Cannot score when resigning.' ) end end + context 'resigned to' do let(:loser) { Fabricate(:user, user_name: 'username') } let(:winner) { Fabricate(:user) } + it 'a player' do expect do expect do expect(message: "#{SlackRubyBot.config.user} resigned to #{winner.display_name}", user: loser.user_id, channel: 'channel').to respond_with_slack_message( "Match has been recorded! #{loser.user_name} resigned against #{winner.display_name}." ) - end.to_not change(Challenge, :count) + end.not_to change(Challenge, :count) end.to change(Match, :count).by(1) match = Match.asc(:_id).last expect(match.winners).to eq [winner] expect(match.losers).to eq [loser] expect(match.resigned?).to be true end + it 'two players' do winner2 = Fabricate(:user, team: team) loser2 = Fabricate(:user, team: team) @@ -50,7 +57,7 @@ expect(message: "#{SlackRubyBot.config.user} resigned to #{winner.user_name} #{winner2.user_name} with #{loser2.user_name}", user: loser.user_id, channel: 'pongbot').to respond_with_slack_message( "Match has been recorded! #{loser.display_name} and #{loser2.display_name} resigned against #{winner.display_name} and #{winner2.display_name}." ) - end.to_not change(Challenge, :count) + end.not_to change(Challenge, :count) end.to change(Match, :count).by(1) match = Match.asc(:_id).last expect(match.winners).to eq [winner2, winner] diff --git a/spec/slack-gamebot/commands/season_spec.rb b/spec/slack-gamebot/commands/season_spec.rb index f4fa45fb..fa612226 100644 --- a/spec/slack-gamebot/commands/season_spec.rb +++ b/spec/slack-gamebot/commands/season_spec.rb @@ -4,35 +4,42 @@ let!(:team) { Fabricate(:team) } let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + shared_examples_for 'season' do context 'no seasons' do it 'seasons' do expect(message: "#{SlackRubyBot.config.user} season").to respond_with_slack_message "There're no seasons." end end + context 'current season' do before do Fabricate(:match) end + it 'returns current season' do current_season = Season.new(team: team) expect(message: "#{SlackRubyBot.config.user} season").to respond_with_slack_message current_season.to_s end + context 'after reset' do before do - ::Season.create!(team: team, created_by: User.first) + Season.create!(team: team, created_by: User.first) end + it 'returns current season' do expect(message: "#{SlackRubyBot.config.user} season").to respond_with_slack_message 'No matches have been recorded.' end end end end + it_behaves_like 'season' context 'with another team' do let!(:team2) { Fabricate(:team) } let!(:match2) { Fabricate(:match, team: team2) } let!(:season2) { Fabricate(:season, team: team2) } + it_behaves_like 'season' end end diff --git a/spec/slack-gamebot/commands/seasons_spec.rb b/spec/slack-gamebot/commands/seasons_spec.rb index 57b47855..059513e7 100644 --- a/spec/slack-gamebot/commands/seasons_spec.rb +++ b/spec/slack-gamebot/commands/seasons_spec.rb @@ -3,23 +3,28 @@ describe SlackGamebot::Commands::Seasons, vcr: { cassette_name: 'user_info' } do let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + shared_examples_for 'seasons' do context 'no seasons' do it 'seasons' do expect(message: "#{SlackRubyBot.config.user} seasons").to respond_with_slack_message "There're no seasons." end end + context 'one season' do before do Array.new(2) { Fabricate(:match, team: team) } challenge = Fabricate(:challenge, challengers: [team.users.asc(:_id).first], challenged: [team.users.asc(:_id).last]) Fabricate(:match, challenge: challenge) end + let!(:season) { Fabricate(:season, team: team) } + it 'seasons' do expect(message: "#{SlackRubyBot.config.user} seasons").to respond_with_slack_message season.to_s end end + context 'two seasons' do let!(:seasons) do Array.new(2) do |n| @@ -30,19 +35,23 @@ Fabricate(:season) end end + it 'returns past seasons and current season' do expect(message: "#{SlackRubyBot.config.user} seasons").to respond_with_slack_message seasons.reverse.map(&:to_s).join("\n") end end + context 'current season' do before do Array.new(2) { Fabricate(:match) } end + it 'returns past seasons and current season' do current_season = Season.new(team: team) expect(message: "#{SlackRubyBot.config.user} seasons").to respond_with_slack_message current_season.to_s end end + context 'current and past season' do let!(:season1) do Array.new(2) { Fabricate(:match) } @@ -54,18 +63,22 @@ Array.new(2) { Fabricate(:match) } Season.new(team: team) end + it 'returns past seasons and current season' do expect(message: "#{SlackRubyBot.config.user} seasons").to respond_with_slack_message [current_season, season1].map(&:to_s).join("\n") end end end + context 'subscribed team' do let!(:team) { Fabricate(:team, subscribed: true) } + it_behaves_like 'seasons' context 'with another team' do let!(:team2) { Fabricate(:team) } let!(:match2) { Fabricate(:match, team: team2) } let!(:season2) { Fabricate(:season, team: team2) } + it_behaves_like 'seasons' end end diff --git a/spec/slack-gamebot/commands/set_spec.rb b/spec/slack-gamebot/commands/set_spec.rb index 0a239071..ffe5db2c 100644 --- a/spec/slack-gamebot/commands/set_spec.rb +++ b/spec/slack-gamebot/commands/set_spec.rb @@ -6,35 +6,41 @@ let(:client) { app.send(:client) } let(:captain) { Fabricate(:user, team: team, user_name: 'username', captain: true) } let(:message_hook) { SlackRubyBot::Hooks::Message.new } + context 'captain' do it 'gives help' do expect(message: "#{SlackRubyBot.config.user} set").to respond_with_slack_message( 'Missing setting, eg. _set gifs off_.' ) end + context 'gifs' do it 'shows current value of GIFs on' do expect(message: "#{SlackRubyBot.config.user} set gifs").to respond_with_slack_message( "GIFs for team #{team.name} are on!" ) end + it 'shows current value of GIFs off' do team.update_attributes!(gifs: false) expect(message: "#{SlackRubyBot.config.user} set gifs").to respond_with_slack_message( "GIFs for team #{team.name} are off." ) end + it 'shows current value of GIFs on' do expect(message: "#{SlackRubyBot.config.user} set gifs").to respond_with_slack_message( "GIFs for team #{team.name} are on!" ) end + it 'shows current value of GIFs off' do team.update_attributes!(gifs: false) expect(message: "#{SlackRubyBot.config.user} set gifs").to respond_with_slack_message( "GIFs for team #{team.name} are off." ) end + it 'enables GIFs' do team.update_attributes!(gifs: false) expect(message: "#{SlackRubyBot.config.user} set gifs on").to respond_with_slack_message( @@ -43,6 +49,7 @@ expect(team.reload.gifs).to be true expect(app.send(:client).send_gifs?).to be true end + it 'disables GIFs with set' do team.update_attributes!(gifs: true) expect(message: "#{SlackRubyBot.config.user} set gifs off").to respond_with_slack_message( @@ -51,6 +58,7 @@ expect(team.reload.gifs).to be false expect(app.send(:client).send_gifs?).to be false end + it 'disables GIFs with unset' do team.update_attributes!(gifs: true) expect(message: "#{SlackRubyBot.config.user} unset gifs").to respond_with_slack_message( @@ -60,29 +68,34 @@ expect(app.send(:client).send_gifs?).to be false end end + context 'unbalanced' do it 'shows current value of unbalanced off' do expect(message: "#{SlackRubyBot.config.user} set unbalanced").to respond_with_slack_message( "Unbalanced challenges for team #{team.name} are off." ) end + it 'shows current value of unbalanced off' do team.update_attributes!(unbalanced: false) expect(message: "#{SlackRubyBot.config.user} set unbalanced").to respond_with_slack_message( "Unbalanced challenges for team #{team.name} are off." ) end + it 'shows current value of unbalanced off' do expect(message: "#{SlackRubyBot.config.user} set unbalanced").to respond_with_slack_message( "Unbalanced challenges for team #{team.name} are off." ) end + it 'shows current value of unbalanced off' do team.update_attributes!(unbalanced: false) expect(message: "#{SlackRubyBot.config.user} set unbalanced").to respond_with_slack_message( "Unbalanced challenges for team #{team.name} are off." ) end + it 'enables unbalanced' do team.update_attributes!(unbalanced: false) expect(message: "#{SlackRubyBot.config.user} set unbalanced on").to respond_with_slack_message( @@ -90,6 +103,7 @@ ) expect(team.reload.unbalanced).to be true end + it 'disables unbalanced with set' do team.update_attributes!(unbalanced: true) expect(message: "#{SlackRubyBot.config.user} set unbalanced off").to respond_with_slack_message( @@ -97,6 +111,7 @@ ) expect(team.reload.unbalanced).to be false end + it 'disables unbalanced with unset' do team.update_attributes!(unbalanced: true) expect(message: "#{SlackRubyBot.config.user} unset unbalanced").to respond_with_slack_message( @@ -105,6 +120,7 @@ expect(team.reload.unbalanced).to be false end end + context 'api' do it 'shows current value of API on' do team.update_attributes!(api: true) @@ -112,30 +128,35 @@ "API for team #{team.name} is on!\n#{team.api_url}" ) end + it 'shows current value of API off' do team.update_attributes!(api: false) expect(message: "#{SlackRubyBot.config.user} set api").to respond_with_slack_message( "API for team #{team.name} is off." ) end + it 'shows current value of API on' do team.update_attributes!(api: true) expect(message: "#{SlackRubyBot.config.user} set api").to respond_with_slack_message( "API for team #{team.name} is on!\n#{team.api_url}" ) end + it 'shows current value of API off' do team.update_attributes!(api: false) expect(message: "#{SlackRubyBot.config.user} set api").to respond_with_slack_message( "API for team #{team.name} is off." ) end + it 'enables API' do expect(message: "#{SlackRubyBot.config.user} set api on").to respond_with_slack_message( "API for team #{team.name} is on!\n#{team.api_url}" ) expect(team.reload.api).to be true end + it 'disables API with set' do team.update_attributes!(api: true) expect(message: "#{SlackRubyBot.config.user} set api off").to respond_with_slack_message( @@ -143,6 +164,7 @@ ) expect(team.reload.api).to be false end + it 'disables API with unset' do team.update_attributes!(api: true) expect(message: "#{SlackRubyBot.config.user} unset api").to respond_with_slack_message( @@ -150,19 +172,23 @@ ) expect(team.reload.api).to be false end + context 'with API_URL' do before do ENV['API_URL'] = 'http://local.api' end + after do ENV.delete 'API_URL' end + it 'shows current value of API on with API URL' do team.update_attributes!(api: true) expect(message: "#{SlackRubyBot.config.user} set api").to respond_with_slack_message( "API for team #{team.name} is on!\nhttp://local.api/teams/#{team.id}" ) end + it 'shows current value of API off without API URL' do team.update_attributes!(api: false) expect(message: "#{SlackRubyBot.config.user} set api").to respond_with_slack_message( @@ -171,26 +197,31 @@ end end end + context 'aliases' do context 'with aliases' do before do team.update_attributes!(aliases: %w[foo bar]) end + it 'shows current value of aliases' do expect(message: "#{SlackRubyBot.config.user} set aliases").to respond_with_slack_message( "Bot aliases for team #{team.name} are foo and bar." ) end end + context 'with aliases' do before do team.update_attributes!(aliases: %w[foo bar]) end + it 'shows current value of aliases' do expect(message: "#{SlackRubyBot.config.user} set aliases").to respond_with_slack_message( "Bot aliases for team #{team.name} are foo and bar." ) end + it 'sets aliases' do expect(message: "#{SlackRubyBot.config.user} set aliases foo bar baz").to respond_with_slack_message( "Bot aliases for team #{team.name} are foo, bar and baz." @@ -198,6 +229,7 @@ expect(team.reload.aliases).to eq %w[foo bar baz] expect(app.send(:client).aliases).to eq %w[foo bar baz] end + it 'sets comma-separated aliases' do expect(message: "#{SlackRubyBot.config.user} set aliases foo,bar").to respond_with_slack_message( "Bot aliases for team #{team.name} are foo and bar." @@ -205,6 +237,7 @@ expect(team.reload.aliases).to eq %w[foo bar] expect(app.send(:client).aliases).to eq %w[foo bar] end + it 'sets comma-separated aliases with extra spaces' do expect(message: "#{SlackRubyBot.config.user} set aliases foo, bar").to respond_with_slack_message( "Bot aliases for team #{team.name} are foo and bar." @@ -212,12 +245,14 @@ expect(team.reload.aliases).to eq %w[foo bar] expect(app.send(:client).aliases).to eq %w[foo bar] end + it 'sets emoji aliases' do expect(message: "#{SlackRubyBot.config.user} set aliases pp :pong:").to respond_with_slack_message( "Bot aliases for team #{team.name} are pp and :pong:." ) expect(team.reload.aliases).to eq ['pp', ':pong:'] end + it 'removes aliases' do expect(message: "#{SlackRubyBot.config.user} unset aliases").to respond_with_slack_message( "Team #{team.name} no longer has bot aliases." @@ -226,6 +261,7 @@ expect(app.send(:client).aliases).to be_empty end end + context 'without aliases' do it 'shows no aliases' do expect(message: "#{SlackRubyBot.config.user} set aliases").to respond_with_slack_message( @@ -234,34 +270,40 @@ end end end + context 'elo' do context 'with a non-default base elo' do before do team.update_attributes!(elo: 1000) end + it 'shows current value of elo' do expect(message: "#{SlackRubyBot.config.user} set elo").to respond_with_slack_message( "Base elo for team #{team.name} is 1000." ) end + it 'sets elo' do expect(message: "#{SlackRubyBot.config.user} set elo 200").to respond_with_slack_message( "Base elo for team #{team.name} is 200." ) expect(team.reload.elo).to eq 200 end + it 'handles errors' do expect(message: "#{SlackRubyBot.config.user} set elo invalid").to respond_with_slack_message( 'Sorry, invalid is not a valid number.' ) expect(team.reload.elo).to eq 1000 end + it 'resets elo with set' do expect(message: "#{SlackRubyBot.config.user} set elo 0").to respond_with_slack_message( "Base elo for team #{team.name} is 0." ) expect(team.reload.elo).to eq 0 end + it 'resets elo with unset' do expect(message: "#{SlackRubyBot.config.user} unset elo").to respond_with_slack_message( "Base elo for team #{team.name} has been unset." @@ -270,60 +312,70 @@ end end end + context 'leaderboard max' do context 'with a non-default leaderboard max' do before do team.update_attributes!(leaderboard_max: 5) end + it 'shows current value of leaderboard max' do expect(message: "#{SlackRubyBot.config.user} set leaderboard max").to respond_with_slack_message( "Leaderboard max for team #{team.name} is 5." ) end + it 'sets leaderboard max' do expect(message: "#{SlackRubyBot.config.user} set leaderboard max 12").to respond_with_slack_message( "Leaderboard max for team #{team.name} is 12." ) expect(team.leaderboard_max).to eq 12 end + it 'sets leaderboard max to a negative number' do expect(message: "#{SlackRubyBot.config.user} set leaderboard max -12").to respond_with_slack_message( "Leaderboard max for team #{team.name} is -12." ) expect(team.leaderboard_max).to eq(-12) end + it 'handles errors' do expect(message: "#{SlackRubyBot.config.user} set leaderboard max invalid").to respond_with_slack_message( 'Sorry, invalid is not a valid number.' ) expect(team.leaderboard_max).to eq 5 end + it 'resets leaderboard max with set 0' do expect(message: "#{SlackRubyBot.config.user} set leaderboard max 0").to respond_with_slack_message( "Leaderboard max for team #{team.name} is not set." ) - expect(team.leaderboard_max).to be nil + expect(team.leaderboard_max).to be_nil end + it 'resets leaderboard max with set infinity' do expect(message: "#{SlackRubyBot.config.user} set leaderboard max infinity").to respond_with_slack_message( "Leaderboard max for team #{team.name} is not set." ) - expect(team.leaderboard_max).to be nil + expect(team.leaderboard_max).to be_nil end + it 'resets leaderboard max with unset' do expect(message: "#{SlackRubyBot.config.user} unset leaderboard max").to respond_with_slack_message( "Leaderboard max for team #{team.name} has been unset." ) - expect(team.leaderboard_max).to be nil + expect(team.leaderboard_max).to be_nil end end end + context 'invalid' do it 'errors set' do expect(message: "#{SlackRubyBot.config.user} set invalid on").to respond_with_slack_message( 'Invalid setting invalid, you can _set gifs on|off_, _set unbalanced on|off_, _api on|off_, _leaderboard max_, _elo_, _nickname_ and _aliases_.' ) end + it 'errors unset' do expect(message: "#{SlackRubyBot.config.user} unset invalid").to respond_with_slack_message( 'Invalid setting invalid, you can _unset gifs_, _api_, _leaderboard max_, _elo_, _nickname_ and _aliases_.' @@ -331,53 +383,62 @@ end end end + context 'not captain' do before do Fabricate(:user, team: team, captain: true) captain.demote! end + context 'gifs' do it 'cannot set GIFs' do expect(message: "#{SlackRubyBot.config.user} set gifs true").to respond_with_slack_message( "You're not a captain, sorry." ) end + it 'can see GIFs value' do expect(message: "#{SlackRubyBot.config.user} set gifs").to respond_with_slack_message( "GIFs for team #{team.name} are on!" ) end end + context 'aliases' do it 'cannot set aliases' do expect(message: "#{SlackRubyBot.config.user} set aliases foo bar").to respond_with_slack_message( "You're not a captain, sorry." ) end + it 'can see aliases' do expect(message: "#{SlackRubyBot.config.user} set aliases").to respond_with_slack_message( "Team #{team.name} does not have any bot aliases." ) end end + context 'elo' do it 'cannot set elo' do expect(message: "#{SlackRubyBot.config.user} set elo 1000").to respond_with_slack_message( "You're not a captain, sorry." ) end + it 'can see elo' do expect(message: "#{SlackRubyBot.config.user} set elo").to respond_with_slack_message( "Base elo for team #{team.name} is 0." ) end end + context 'leaderboard max' do it 'cannot set leaderboard max' do expect(message: "#{SlackRubyBot.config.user} set leaderboard max 3").to respond_with_slack_message( "You're not a captain, sorry." ) end + it 'can see leaderboard max' do expect(message: "#{SlackRubyBot.config.user} set leaderboard max").to respond_with_slack_message( "Leaderboard max for team #{team.name} is not set." @@ -385,8 +446,10 @@ end end end + context 'nickname' do let(:user) { Fabricate(:user, team: team, user_name: 'username') } + context 'with no nickname' do it 'shows that the user has no nickname' do expect(message: "#{SlackRubyBot.config.user} set nickname", user: user.user_id).to respond_with_slack_message( @@ -394,6 +457,7 @@ ) end end + context 'without a nickname set' do it 'sets nickname' do expect(message: "#{SlackRubyBot.config.user} set nickname john doe", user: user.user_id).to respond_with_slack_message( @@ -401,12 +465,14 @@ ) expect(user.reload.nickname).to eq 'john doe' end + it 'does not unset nickname' do expect(message: "#{SlackRubyBot.config.user} unset nickname", user: user.user_id).to respond_with_slack_message( "You don't have a nickname set, #{user.slack_mention}." ) - expect(user.reload.nickname).to be nil + expect(user.reload.nickname).to be_nil end + it 'sets emoji nickname' do expect(message: "#{SlackRubyBot.config.user} set nickname :dancer:", user: user.user_id).to respond_with_slack_message( "Your nickname is now *:dancer:*, #{user.slack_mention}." @@ -414,44 +480,51 @@ expect(user.reload.nickname).to eq ':dancer:' end end + context 'with a nickname set' do before do user.update_attributes!(nickname: 'bob') end + it 'shows current value of nickname' do expect(message: "#{SlackRubyBot.config.user} set nickname", user: user.user_id).to respond_with_slack_message( "Your nickname is *bob*, #{user.slack_mention}." ) end + it 'sets nickname' do expect(message: "#{SlackRubyBot.config.user} set nickname john doe", user: user.user_id).to respond_with_slack_message( "Your nickname is now *john doe*, #{user.slack_mention}." ) expect(user.reload.nickname).to eq 'john doe' end + it 'unsets nickname' do expect(message: "#{SlackRubyBot.config.user} unset nickname", user: user.user_id).to respond_with_slack_message( "You don't have a nickname set anymore, #{user.slack_mention}." ) - expect(user.reload.nickname).to be nil + expect(user.reload.nickname).to be_nil end + it 'cannot set nickname unless captain' do expect(message: "#{SlackRubyBot.config.user} set nickname #{captain.slack_mention} :dancer:", user: user.user_id).to respond_with_slack_message( "You're not a captain, sorry." ) end + it 'sets nickname for another user' do expect(message: "#{SlackRubyBot.config.user} set nickname #{user.slack_mention} john doe", user: captain.user_id).to respond_with_slack_message( "Your nickname is now *john doe*, #{user.slack_mention}." ) expect(user.reload.nickname).to eq 'john doe' end + it 'unsets nickname for another user' do user.update_attributes!(nickname: 'bob') expect(message: "#{SlackRubyBot.config.user} unset nickname #{user.slack_mention}", user: captain.user_id).to respond_with_slack_message( "You don't have a nickname set anymore, #{user.slack_mention}." ) - expect(user.reload.nickname).to be nil + expect(user.reload.nickname).to be_nil end end end diff --git a/spec/slack-gamebot/commands/subscription_spec.rb b/spec/slack-gamebot/commands/subscription_spec.rb index 96058a29..bb9e1f28 100644 --- a/spec/slack-gamebot/commands/subscription_spec.rb +++ b/spec/slack-gamebot/commands/subscription_spec.rb @@ -3,26 +3,31 @@ describe SlackGamebot::Commands::Subscription, vcr: { cassette_name: 'user_info' } do let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + shared_examples_for 'subscription' do context 'on trial' do before do team.update_attributes!(subscribed: false, subscribed_at: nil) end + it 'displays subscribe message' do expect(message: "#{SlackRubyBot.config.user} subscription").to respond_with_slack_message team.trial_message end end + context 'with subscribed_at' do it 'displays subscription info' do customer_info = "Subscriber since #{team.subscribed_at.strftime('%B %d, %Y')}." expect(message: "#{SlackRubyBot.config.user} subscription").to respond_with_slack_message customer_info end end + context 'with a plan' do - include_context :stripe_mock + include_context 'stripe mock' before do stripe_helper.create_plan(id: 'slack-playplay-yearly', amount: 2999, name: 'Plan') end + context 'a customer' do let!(:customer) do Stripe::Customer.create( @@ -31,9 +36,11 @@ email: 'foo@bar.com' ) end + before do team.update_attributes!(subscribed: true, stripe_customer_id: customer['id']) end + it 'displays subscription info' do card = customer.sources.first current_period_end = Time.at(customer.subscriptions.first.current_period_end).strftime('%B %d, %Y') @@ -45,11 +52,13 @@ ].join("\n") expect(message: "#{SlackRubyBot.config.user} subscription").to respond_with_slack_message customer_info end + context 'past due subscription' do before do customer.subscriptions.data.first['status'] = 'past_due' allow(Stripe::Customer).to receive(:retrieve).and_return(customer) end + it 'displays subscription info' do customer_info = "Customer since #{Time.at(customer.created).strftime('%B %d, %Y')}." customer_info += "\nPast Due subscription created November 03, 2016 to Plan ($29.99)." @@ -62,11 +71,14 @@ end end end + context 'subscribed team' do let!(:team) { Fabricate(:team, subscribed: true) } + it_behaves_like 'subscription' context 'with another team' do let!(:team2) { Fabricate(:team) } + it_behaves_like 'subscription' end end diff --git a/spec/slack-gamebot/commands/sucks_spec.rb b/spec/slack-gamebot/commands/sucks_spec.rb index f76807a7..8037401d 100644 --- a/spec/slack-gamebot/commands/sucks_spec.rb +++ b/spec/slack-gamebot/commands/sucks_spec.rb @@ -5,38 +5,45 @@ let(:app) { SlackGamebot::Server.new(team: team) } let(:user) { Fabricate(:user) } let(:client) { app.send(:client) } + it 'sucks' do expect(message: "#{SlackRubyBot.config.user} sucks").to respond_with_slack_message( 'No <@user>, you suck!' ) end + it 'suck' do expect(message: "#{SlackRubyBot.config.user} you suck").to respond_with_slack_message( 'No <@user>, you suck!' ) end + it 'sucks!' do expect(message: "#{SlackRubyBot.config.user} sucks!").to respond_with_slack_message( 'No <@user>, you suck!' ) end + it 'really sucks!' do expect(message: "#{SlackRubyBot.config.user} you suck!").to respond_with_slack_message( 'No <@user>, you suck!' ) end + it 'does not conflict with a player name that contains suck' do allow(client.web_client).to receive(:users_info) expect(message: "#{SlackRubyBot.config.user} challenge suckarov", user: user.user_id, channel: 'pongbot').to respond_with_slack_message( "I don't know who suckarov is! Ask them to _register_." ) end + it 'sucks for someone with many losses' do allow_any_instance_of(User).to receive(:losses).and_return(6) expect(message: "#{SlackRubyBot.config.user} sucks").to respond_with_slack_message( 'No <@user>, with 6 losses, you suck!' ) end + it 'sucks for a poorly ranked user' do allow_any_instance_of(User).to receive(:rank).and_return(4) expect(message: "#{SlackRubyBot.config.user} sucks").to respond_with_slack_message( diff --git a/spec/slack-gamebot/commands/taunt_spec.rb b/spec/slack-gamebot/commands/taunt_spec.rb index ce59ae82..9d1f3ced 100644 --- a/spec/slack-gamebot/commands/taunt_spec.rb +++ b/spec/slack-gamebot/commands/taunt_spec.rb @@ -5,18 +5,21 @@ let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } let(:user) { Fabricate(:user, user_name: 'username') } + it 'taunts one person by user id' do victim = Fabricate(:user, team: team) expect(message: "#{SlackRubyBot.config.user} taunt <@#{victim.user_id}>", user: user.user_id).to respond_with_slack_message( "#{user.user_name} says that #{victim.user_name} sucks at #{client.name}!" ) end + it 'taunts one person by user name' do victim = Fabricate(:user, team: team) expect(message: "#{SlackRubyBot.config.user} taunt #{victim.user_name}", user: user.user_id).to respond_with_slack_message( "#{user.user_name} says that #{victim.user_name} sucks at #{client.name}!" ) end + it 'taunts multiple users by user id' do victim1 = Fabricate(:user, team: team) victim2 = Fabricate(:user, team: team) @@ -25,6 +28,7 @@ "#{user.user_name} says that #{victim1.user_name}, #{victim2.user_name} and #{victim3.user_name} suck at #{client.name}!" ) end + it 'taunts multiple users by user name' do victim1 = Fabricate(:user, team: team) victim2 = Fabricate(:user, team: team) @@ -33,6 +37,7 @@ "#{user.user_name} says that #{victim1.user_name}, #{victim2.user_name} and #{victim3.user_name} suck at #{client.name}!" ) end + it 'no entered user to taunt' do expect(message: "#{SlackRubyBot.config.user} taunt", user: user.user_id).to respond_with_slack_message( 'Please provide a user name to taunt.' diff --git a/spec/slack-gamebot/commands/team_spec.rb b/spec/slack-gamebot/commands/team_spec.rb index 013f9d8d..93fd3a10 100644 --- a/spec/slack-gamebot/commands/team_spec.rb +++ b/spec/slack-gamebot/commands/team_spec.rb @@ -4,22 +4,27 @@ let!(:team) { Fabricate(:team) } let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + context 'no users' do it 'team' do allow(User).to receive(:find_create_or_update_by_slack_id!) expect(message: "#{SlackRubyBot.config.user} team").to respond_with_slack_message "Team _#{team.name}_ (#{team.team_id})." end end + context 'with a captain' do let!(:user) { Fabricate(:user, team: team, user_name: 'username', captain: true) } + it 'team' do expect(message: "#{SlackRubyBot.config.user} team").to respond_with_slack_message "Team _#{team.name}_ (#{team.team_id}), captain username." end end + context 'with two captains' do before do Array.new(2) { Fabricate(:user, team: team, captain: true) } end + it 'team' do expect(message: "#{SlackRubyBot.config.user} team").to respond_with_slack_message "Team _#{team.name}_ (#{team.team_id}), captains #{team.captains.map(&:display_name).and}." end diff --git a/spec/slack-gamebot/commands/unknown_spec.rb b/spec/slack-gamebot/commands/unknown_spec.rb index 8d352ccb..5fecb059 100644 --- a/spec/slack-gamebot/commands/unknown_spec.rb +++ b/spec/slack-gamebot/commands/unknown_spec.rb @@ -5,11 +5,13 @@ let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } let(:message_hook) { SlackRubyBot::Hooks::Message.new } + it 'invalid command' do expect(message: "#{SlackRubyBot.config.user} foobar").to respond_with_slack_message("Sorry <@user>, I don't understand that command!") end + it 'does not respond to sad face' do - expect(SlackRubyBot::Commands::Base).to_not receive(:send_message) + expect(SlackRubyBot::Commands::Base).not_to receive(:send_message) message_hook.call(client, Hashie::Mash.new(text: ':((')) end end diff --git a/spec/slack-gamebot/commands/unregister_spec.rb b/spec/slack-gamebot/commands/unregister_spec.rb index 5e0cfdf9..6b72d89a 100644 --- a/spec/slack-gamebot/commands/unregister_spec.rb +++ b/spec/slack-gamebot/commands/unregister_spec.rb @@ -5,38 +5,45 @@ let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } let!(:team) { Fabricate(:team) } + it 'requires a captain to unregister someone' do Fabricate(:user, captain: true, team: team) user = Fabricate(:user) expect(message: "#{SlackRubyBot.config.user} unregister #{user.user_name}").to respond_with_slack_message("You're not a captain, sorry.") end + it 'registers, then unregisters a previously unknown user' do expect do expect(message: "#{SlackRubyBot.config.user} unregister", user: 'user1').to respond_with_slack_message("I've removed <@user1> from the leaderboard.") - end.to change(::User, :count).by(1) + end.to change(User, :count).by(1) expect(User.where(user_id: 'user1').first.registered).to be false end + it 'cannot unregister an unknown user by name' do captain = Fabricate(:user, captain: true, team: team) allow(client.web_client).to receive(:users_info) user = Fabricate(:user, team: Fabricate(:team)) # another user in another team expect(message: "#{SlackRubyBot.config.user} unregister #{user.user_name}", user: captain.user_id).to respond_with_slack_message("I don't know who #{user.user_name} is! Ask them to _register_.") end + it 'unregisters self' do user = Fabricate(:user, user_id: 'user') expect(message: "#{SlackRubyBot.config.user} unregister", user: user.user_id).to respond_with_slack_message("I've removed <@user> from the leaderboard.") expect(user.reload.registered).to be false end + it 'unregisters self via me' do user = Fabricate(:user, user_id: 'user') expect(message: "#{SlackRubyBot.config.user} unregister me", user: user.user_id).to respond_with_slack_message("I've removed <@user> from the leaderboard.") expect(user.reload.registered).to be false end + it 'unregisters another user' do user = Fabricate(:user) expect(message: "#{SlackRubyBot.config.user} unregister #{user.user_name}", user: 'user').to respond_with_slack_message("I've removed <@#{user.user_id}> from the leaderboard.") expect(user.reload.registered).to be false end + it 'unregisters multiple users' do user1 = Fabricate(:user) user2 = Fabricate(:user) diff --git a/spec/slack-gamebot/commands/unsubscribe_spec.rb b/spec/slack-gamebot/commands/unsubscribe_spec.rb index dccf00d2..c69cc2c8 100644 --- a/spec/slack-gamebot/commands/unsubscribe_spec.rb +++ b/spec/slack-gamebot/commands/unsubscribe_spec.rb @@ -3,28 +3,34 @@ describe SlackGamebot::Commands::Unsubscribe, vcr: { cassette_name: 'user_info' } do let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + shared_examples_for 'unsubscribe' do context 'on trial' do before do team.update_attributes!(subscribed: false, subscribed_at: nil) end + it 'displays all set message' do expect(message: "#{SlackRubyBot.config.user} unsubscribe").to respond_with_slack_message "You don't have a paid subscription, all set." end end + context 'with subscribed_at' do before do team.update_attributes!(subscribed: true, subscribed_at: 1.year.ago) end + it 'displays subscription info' do expect(message: "#{SlackRubyBot.config.user} unsubscribe").to respond_with_slack_message "You don't have a paid subscription, all set." end end + context 'with a plan' do - include_context :stripe_mock + include_context 'stripe mock' before do stripe_helper.create_plan(id: 'slack-playplay-yearly', amount: 2999, name: 'Plan') end + context 'a customer' do let!(:customer) do Stripe::Customer.create( @@ -33,11 +39,13 @@ email: 'foo@bar.com' ) end + let(:active_subscription) { team.active_stripe_subscription } + let(:current_period_end) { Time.at(active_subscription.current_period_end).strftime('%B %d, %Y') } + before do team.update_attributes!(subscribed: true, stripe_customer_id: customer['id']) end - let(:active_subscription) { team.active_stripe_subscription } - let(:current_period_end) { Time.at(active_subscription.current_period_end).strftime('%B %d, %Y') } + it 'displays subscription info' do customer_info = [ "Subscribed to Plan ($29.99), will auto-renew on #{current_period_end}.", @@ -45,23 +53,28 @@ ].join("\n") expect(message: "#{SlackRubyBot.config.user} unsubscribe").to respond_with_slack_message customer_info end + it 'cannot unsubscribe with an invalid subscription id' do expect(message: "#{SlackRubyBot.config.user} unsubscribe xyz").to respond_with_slack_message 'Sorry, I cannot find a subscription with "xyz".' end + it 'unsubscribes' do expect(message: "#{SlackRubyBot.config.user} unsubscribe #{active_subscription.id}").to respond_with_slack_message 'Successfully canceled auto-renew for Plan ($29.99).' team.reload expect(team.subscribed).to be true - expect(team.stripe_customer_id).to_not be nil + expect(team.stripe_customer_id).not_to be_nil end end end end + context 'subscribed team' do let!(:team) { Fabricate(:team, subscribed: true) } + it_behaves_like 'unsubscribe' context 'with another team' do let!(:team2) { Fabricate(:team) } + it_behaves_like 'unsubscribe' end end diff --git a/spec/slack-gamebot/server_spec.rb b/spec/slack-gamebot/server_spec.rb index 8f7b7f26..b9454ca7 100644 --- a/spec/slack-gamebot/server_spec.rb +++ b/spec/slack-gamebot/server_spec.rb @@ -4,50 +4,62 @@ let(:team) { Fabricate(:team) } let(:app) { SlackGamebot::Server.new(team: team) } let(:client) { app.send(:client) } + context 'send_gifs' do context 'default' do let(:team) { Fabricate(:team) } + it 'true' do expect(app.send(:client).send_gifs?).to be true end end + context 'on' do let(:team) { Fabricate(:team, gifs: true) } + it 'true' do expect(app.send(:client).send_gifs?).to be true end end + context 'off' do let(:team) { Fabricate(:team, gifs: false) } + it 'false' do expect(app.send(:client).send_gifs?).to be false end end end + context 'aliases' do let(:game) { Fabricate(:game, name: 'game', aliases: []) } let(:team) { Fabricate(:team, game: game, aliases: %w[t1 t2]) } + it 'combines game name and team aliases' do expect(app.send(:client).aliases).to eq %w[game t1 t2] end end + context 'hooks' do let(:user) { Fabricate(:user, team: team) } + it 'renames user' do app.send(:hook_blocks)[:user_change].each do |hook| hook.call(client, Hashie::Mash.new(type: 'user_change', user: { id: user.user_id, name: 'updated' })) end expect(user.reload.user_name).to eq('updated') end + it 'does not touch a user with the same name' do allow(User).to receive(:where).and_return([user]) app.send(:hook_blocks)[:user_change].each do |hook| hook.call(client, Hashie::Mash.new(type: 'user_change', user: { id: user.user_id, name: user.user_name })) end - expect(user).to_not receive(:update_attributes!) + expect(user).not_to receive(:update_attributes!) end end - context '#channel_joined' do + + describe '#channel_joined' do it 'sends a welcome message' do allow(client).to receive_message_chain(:self, :name).and_return('bot') expect(client).to receive(:say).with(channel: 'C12345', text: [ diff --git a/spec/slack-gamebot/service_spec.rb b/spec/slack-gamebot/service_spec.rb index e00abd02..6f5ee63e 100644 --- a/spec/slack-gamebot/service_spec.rb +++ b/spec/slack-gamebot/service_spec.rb @@ -1,47 +1,58 @@ require 'spec_helper' describe SlackRubyBotServer::Service do - context '#url' do + describe '#url' do before do @rack_env = ENV.fetch('RACK_ENV', nil) end + after do ENV['RACK_ENV'] = @rack_env end + it 'defaults to playplay.io in production' do expect(SlackRubyBotServer::Service.url).to eq 'https://www.playplay.io' end + context 'in development' do before do ENV['RACK_ENV'] = 'development' end + it 'defaults to localhost' do expect(SlackRubyBotServer::Service.url).to eq 'http://localhost:5000' end end + context 'when set' do before do ENV['URL'] = 'updated' end + after do ENV.delete('URL') end + it 'defaults to ENV' do expect(SlackRubyBotServer::Service.url).to eq 'updated' end end end - context '#api_url' do + + describe '#api_url' do it 'defaults to playplay.io in production' do expect(SlackRubyBotServer::Service.api_url).to eq 'https://www.playplay.io/api' end + context 'when set' do before do ENV['API_URL'] = 'updated' end + after do ENV.delete 'API_URL' end + it 'defaults to ENV' do expect(SlackRubyBotServer::Service.api_url).to eq 'updated' end diff --git a/spec/slack-gamebot/version_spec.rb b/spec/slack-gamebot/version_spec.rb index 49c22c9a..0f6d268a 100644 --- a/spec/slack-gamebot/version_spec.rb +++ b/spec/slack-gamebot/version_spec.rb @@ -2,6 +2,6 @@ describe SlackGamebot do it 'has a version' do - expect(SlackGamebot::VERSION).to_not be nil + expect(SlackGamebot::VERSION).not_to be_nil end end diff --git a/spec/support/api/endpoints/it_behaves_like_a_cursor_api.rb b/spec/support/api/endpoints/it_behaves_like_a_cursor_api.rb index 135a6034..e166f544 100644 --- a/spec/support/api/endpoints/it_behaves_like_a_cursor_api.rb +++ b/spec/support/api/endpoints/it_behaves_like_a_cursor_api.rb @@ -48,8 +48,9 @@ context 'total count' do it "doesn't return total_count" do response = client.send(model_ps, cursor_params) - expect(response).to_not respond_to(:total_count) + expect(response).not_to respond_to(:total_count) end + it 'returns total_count when total_count query string is specified' do response = client.send(model_ps, cursor_params.merge(total_count: true)) expect(response.total_count).to eq model.all.count diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb index 56bba3a6..4ccef29c 100644 --- a/spec/support/database_cleaner.rb +++ b/spec/support/database_cleaner.rb @@ -10,7 +10,7 @@ Mongoid.purge! end - config.around :each do |example| + config.around do |example| DatabaseCleaner.cleaning do example.run end diff --git a/spec/support/stripe.rb b/spec/support/stripe.rb index f267e4fe..1e8aa78c 100644 --- a/spec/support/stripe.rb +++ b/spec/support/stripe.rb @@ -1,8 +1,9 @@ -RSpec.shared_context :stripe_mock do +RSpec.shared_context 'stripe mock' do let(:stripe_helper) { StripeMock.create_test_helper } before do StripeMock.start end + after do StripeMock.stop end