We're all about community. And what better way to connect than with snacks from around the world. So grab a snack and let your taste buds travel the world.
+
+
+
+ <%= render partial: "add_review", locals: {url: create_review_path, method: :post } %>
+
+
+
diff --git a/bin/bundle b/bin/bundle
new file mode 100755
index 0000000000..f19acf5b5c
--- /dev/null
+++ b/bin/bundle
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
+load Gem.bin_path('bundler', 'bundle')
diff --git a/bin/rails b/bin/rails
new file mode 100755
index 0000000000..5badb2fde0
--- /dev/null
+++ b/bin/rails
@@ -0,0 +1,9 @@
+#!/usr/bin/env ruby
+begin
+ load File.expand_path('../spring', __FILE__)
+rescue LoadError => e
+ raise unless e.message.include?('spring')
+end
+APP_PATH = File.expand_path('../config/application', __dir__)
+require_relative '../config/boot'
+require 'rails/commands'
diff --git a/bin/rake b/bin/rake
new file mode 100755
index 0000000000..d87d5f5781
--- /dev/null
+++ b/bin/rake
@@ -0,0 +1,9 @@
+#!/usr/bin/env ruby
+begin
+ load File.expand_path('../spring', __FILE__)
+rescue LoadError => e
+ raise unless e.message.include?('spring')
+end
+require_relative '../config/boot'
+require 'rake'
+Rake.application.run
diff --git a/bin/setup b/bin/setup
new file mode 100755
index 0000000000..94fd4d7977
--- /dev/null
+++ b/bin/setup
@@ -0,0 +1,36 @@
+#!/usr/bin/env ruby
+require 'fileutils'
+include FileUtils
+
+# path to your application root.
+APP_ROOT = File.expand_path('..', __dir__)
+
+def system!(*args)
+ system(*args) || abort("\n== Command #{args} failed ==")
+end
+
+chdir APP_ROOT do
+ # This script is a starting point to setup your application.
+ # Add necessary setup steps to this file.
+
+ puts '== Installing dependencies =='
+ system! 'gem install bundler --conservative'
+ system('bundle check') || system!('bundle install')
+
+ # Install JavaScript dependencies if using Yarn
+ # system('bin/yarn')
+
+ # puts "\n== Copying sample files =="
+ # unless File.exist?('config/database.yml')
+ # cp 'config/database.yml.sample', 'config/database.yml'
+ # end
+
+ puts "\n== Preparing database =="
+ system! 'bin/rails db:setup'
+
+ puts "\n== Removing old logs and tempfiles =="
+ system! 'bin/rails log:clear tmp:clear'
+
+ puts "\n== Restarting application server =="
+ system! 'bin/rails restart'
+end
diff --git a/bin/spring b/bin/spring
new file mode 100755
index 0000000000..fb2ec2ebb4
--- /dev/null
+++ b/bin/spring
@@ -0,0 +1,17 @@
+#!/usr/bin/env ruby
+
+# This file loads spring without using Bundler, in order to be fast.
+# It gets overwritten when you run the `spring binstub` command.
+
+unless defined?(Spring)
+ require 'rubygems'
+ require 'bundler'
+
+ lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
+ spring = lockfile.specs.detect { |spec| spec.name == "spring" }
+ if spring
+ Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
+ gem 'spring', spring.version
+ require 'spring/binstub'
+ end
+end
diff --git a/bin/update b/bin/update
new file mode 100755
index 0000000000..58bfaed518
--- /dev/null
+++ b/bin/update
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+require 'fileutils'
+include FileUtils
+
+# path to your application root.
+APP_ROOT = File.expand_path('..', __dir__)
+
+def system!(*args)
+ system(*args) || abort("\n== Command #{args} failed ==")
+end
+
+chdir APP_ROOT do
+ # This script is a way to update your development environment automatically.
+ # Add necessary update steps to this file.
+
+ puts '== Installing dependencies =='
+ system! 'gem install bundler --conservative'
+ system('bundle check') || system!('bundle install')
+
+ # Install JavaScript dependencies if using Yarn
+ # system('bin/yarn')
+
+ puts "\n== Updating database =="
+ system! 'bin/rails db:migrate'
+
+ puts "\n== Removing old logs and tempfiles =="
+ system! 'bin/rails log:clear tmp:clear'
+
+ puts "\n== Restarting application server =="
+ system! 'bin/rails restart'
+end
diff --git a/bin/yarn b/bin/yarn
new file mode 100755
index 0000000000..460dd565b4
--- /dev/null
+++ b/bin/yarn
@@ -0,0 +1,11 @@
+#!/usr/bin/env ruby
+APP_ROOT = File.expand_path('..', __dir__)
+Dir.chdir(APP_ROOT) do
+ begin
+ exec "yarnpkg", *ARGV
+ rescue Errno::ENOENT
+ $stderr.puts "Yarn executable was not detected in the system."
+ $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
+ exit 1
+ end
+end
diff --git a/config.ru b/config.ru
new file mode 100644
index 0000000000..f7ba0b527b
--- /dev/null
+++ b/config.ru
@@ -0,0 +1,5 @@
+# This file is used by Rack-based servers to start the application.
+
+require_relative 'config/environment'
+
+run Rails.application
diff --git a/config/application.rb b/config/application.rb
new file mode 100644
index 0000000000..5c09a3eefc
--- /dev/null
+++ b/config/application.rb
@@ -0,0 +1,25 @@
+require_relative 'boot'
+
+require 'rails/all'
+
+# Require the gems listed in Gemfile, including any gems
+# you've limited to :test, :development, or :production.
+Bundler.require(*Rails.groups)
+
+module Betsy
+ class Application < Rails::Application
+ config.generators do |g|
+ # Force new test files to be generated in the minitest-spec style
+ g.test_framework :minitest, spec: true
+ # Always use .js files, never .coffee
+ g.javascript_engine :js
+ end
+ # Initialize configuration defaults for originally generated Rails version.
+ config.load_defaults 5.2
+
+ # Settings in config/environments/* take precedence over those specified here.
+ # Application configuration can go into files in config/initializers
+ # -- all .rb files in that directory are automatically loaded after loading
+ # the framework and any gems in your application.
+ end
+end
diff --git a/config/boot.rb b/config/boot.rb
new file mode 100644
index 0000000000..b9e460cef3
--- /dev/null
+++ b/config/boot.rb
@@ -0,0 +1,4 @@
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
+
+require 'bundler/setup' # Set up gems listed in the Gemfile.
+require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
diff --git a/config/cable.yml b/config/cable.yml
new file mode 100644
index 0000000000..dd2a324c68
--- /dev/null
+++ b/config/cable.yml
@@ -0,0 +1,10 @@
+development:
+ adapter: async
+
+test:
+ adapter: async
+
+production:
+ adapter: redis
+ url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
+ channel_prefix: betsy_production
diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc
new file mode 100644
index 0000000000..61407bbea7
--- /dev/null
+++ b/config/credentials.yml.enc
@@ -0,0 +1 @@
+r2T3MHrRccDAg2gEsgq7qqQobEe1mu2Iz8BsUHego82K1cGHsziS1a6+40uaKKViAptoR66NxI6nBJPJiTRgwpDPAYvc7JqcGL3LK19CCCiEd/X++9ERMjfnAVetB3vQ3HnbMKyU6Io1uVG15T74a1OT/Z6DmfHcy/rKlE4Y3HcV82l4/mHf0Aiq7smZd2uvnOKOQXzYXQ3zePb5m4z21rCfe735LdiKUwBQ0fCn4mSe0WfM8dYAPFELGMHrmcguu3FVpeouKSzcD1YDxdv3+/Bs0bJYkiPVzvAgl4EZseUDoqMDJ8S6AkjY5i9Ue0mrgI4oQL/hlVE3mjBKxIW1zroOr2Sczn3S9bF99hCZdUwOS/ru7nCEyoRCe72rqX/Obu+zhqUooNBIdMhd33j3ayqZiagcwBB01YAa--6g2qSbK+bS2V5Xm9--bYW0b20zR7xHVJeDEE6ziw==
\ No newline at end of file
diff --git a/config/database.yml b/config/database.yml
new file mode 100644
index 0000000000..6903bb6083
--- /dev/null
+++ b/config/database.yml
@@ -0,0 +1,85 @@
+# PostgreSQL. Versions 9.1 and up are supported.
+#
+# Install the pg driver:
+# gem install pg
+# On OS X with Homebrew:
+# gem install pg -- --with-pg-config=/usr/local/bin/pg_config
+# On OS X with MacPorts:
+# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config
+# On Windows:
+# gem install pg
+# Choose the win32 build.
+# Install PostgreSQL and put its /bin directory on your path.
+#
+# Configure Using Gemfile
+# gem 'pg'
+#
+default: &default
+ adapter: postgresql
+ encoding: unicode
+ # For details on connection pooling, see Rails configuration guide
+ # http://guides.rubyonrails.org/configuring.html#database-pooling
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
+
+development:
+ <<: *default
+ database: betsy_development
+
+ # The specified database role being used to connect to postgres.
+ # To create additional roles in postgres see `$ createuser --help`.
+ # When left blank, postgres will use the default role. This is
+ # the same name as the operating system user that initialized the database.
+ #username: betsy
+
+ # The password associated with the postgres role (username).
+ #password:
+
+ # Connect on a TCP socket. Omitted by default since the client uses a
+ # domain socket that doesn't need configuration. Windows does not have
+ # domain sockets, so uncomment these lines.
+ #host: localhost
+
+ # The TCP port the server listens on. Defaults to 5432.
+ # If your server runs on a different port number, change accordingly.
+ #port: 5432
+
+ # Schema search path. The server defaults to $user,public
+ #schema_search_path: myapp,sharedapp,public
+
+ # Minimum log levels, in increasing order:
+ # debug5, debug4, debug3, debug2, debug1,
+ # log, notice, warning, error, fatal, and panic
+ # Defaults to warning.
+ #min_messages: notice
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: betsy_test
+
+# As with config/secrets.yml, you never want to store sensitive information,
+# like your database password, in your source code. If your source code is
+# ever seen by anyone, they now have access to your database.
+#
+# Instead, provide the password as a unix environment variable when you boot
+# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# for a full rundown on how to provide these environment variables in a
+# production deployment.
+#
+# On Heroku and other platform providers, you may have a full connection URL
+# available as an environment variable. For example:
+#
+# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
+#
+# You can use this database configuration with:
+#
+# production:
+# url: <%= ENV['DATABASE_URL'] %>
+#
+production:
+ <<: *default
+ database: betsy_production
+ username: betsy
+ password: <%= ENV['BETSY_DATABASE_PASSWORD'] %>
diff --git a/config/environment.rb b/config/environment.rb
new file mode 100644
index 0000000000..426333bb46
--- /dev/null
+++ b/config/environment.rb
@@ -0,0 +1,5 @@
+# Load the Rails application.
+require_relative 'application'
+
+# Initialize the Rails application.
+Rails.application.initialize!
diff --git a/config/environments/development.rb b/config/environments/development.rb
new file mode 100644
index 0000000000..1311e3e4ef
--- /dev/null
+++ b/config/environments/development.rb
@@ -0,0 +1,61 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # In the development environment your application's code is reloaded on
+ # every request. This slows down response time but is perfect for development
+ # since you don't have to restart the web server when you make code changes.
+ config.cache_classes = false
+
+ # Do not eager load code on boot.
+ config.eager_load = false
+
+ # Show full error reports.
+ config.consider_all_requests_local = true
+
+ # Enable/disable caching. By default caching is disabled.
+ # Run rails dev:cache to toggle caching.
+ if Rails.root.join('tmp', 'caching-dev.txt').exist?
+ config.action_controller.perform_caching = true
+
+ config.cache_store = :memory_store
+ config.public_file_server.headers = {
+ 'Cache-Control' => "public, max-age=#{2.days.to_i}"
+ }
+ else
+ config.action_controller.perform_caching = false
+
+ config.cache_store = :null_store
+ end
+
+ # Store uploaded files on the local file system (see config/storage.yml for options)
+ config.active_storage.service = :local
+
+ # Don't care if the mailer can't send.
+ config.action_mailer.raise_delivery_errors = false
+
+ config.action_mailer.perform_caching = false
+
+ # Print deprecation notices to the Rails logger.
+ config.active_support.deprecation = :log
+
+ # Raise an error on page load if there are pending migrations.
+ config.active_record.migration_error = :page_load
+
+ # Highlight code that triggered database queries in logs.
+ config.active_record.verbose_query_logs = true
+
+ # Debug mode disables concatenation and preprocessing of assets.
+ # This option may cause significant delays in view rendering with a large
+ # number of complex assets.
+ config.assets.debug = true
+
+ # Suppress logger output for asset requests.
+ config.assets.quiet = true
+
+ # Raises error for missing translations
+ # config.action_view.raise_on_missing_translations = true
+
+ # Use an evented file watcher to asynchronously detect changes in source code,
+ # routes, locales, etc. This feature depends on the listen gem.
+ config.file_watcher = ActiveSupport::EventedFileUpdateChecker
+end
diff --git a/config/environments/production.rb b/config/environments/production.rb
new file mode 100644
index 0000000000..5f6f3058c6
--- /dev/null
+++ b/config/environments/production.rb
@@ -0,0 +1,94 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # Code is not reloaded between requests.
+ config.cache_classes = true
+
+ # Eager load code on boot. This eager loads most of Rails and
+ # your application in memory, allowing both threaded web servers
+ # and those relying on copy on write to perform better.
+ # Rake tasks automatically ignore this option for performance.
+ config.eager_load = true
+
+ # Full error reports are disabled and caching is turned on.
+ config.consider_all_requests_local = false
+ config.action_controller.perform_caching = true
+
+ # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
+ # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
+ # config.require_master_key = true
+
+ # Disable serving static files from the `/public` folder by default since
+ # Apache or NGINX already handles this.
+ config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
+
+ # Compress JavaScripts and CSS.
+ config.assets.js_compressor = :uglifier
+ # config.assets.css_compressor = :sass
+
+ # Do not fallback to assets pipeline if a precompiled asset is missed.
+ config.assets.compile = false
+
+ # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
+
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server.
+ # config.action_controller.asset_host = 'http://assets.example.com'
+
+ # Specifies the header that your server uses for sending files.
+ # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
+
+ # Store uploaded files on the local file system (see config/storage.yml for options)
+ config.active_storage.service = :local
+
+ # Mount Action Cable outside main process or domain
+ # config.action_cable.mount_path = nil
+ # config.action_cable.url = 'wss://example.com/cable'
+ # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
+
+ # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
+ # config.force_ssl = true
+
+ # Use the lowest log level to ensure availability of diagnostic information
+ # when problems arise.
+ config.log_level = :debug
+
+ # Prepend all log lines with the following tags.
+ config.log_tags = [ :request_id ]
+
+ # Use a different cache store in production.
+ # config.cache_store = :mem_cache_store
+
+ # Use a real queuing backend for Active Job (and separate queues per environment)
+ # config.active_job.queue_adapter = :resque
+ # config.active_job.queue_name_prefix = "betsy_#{Rails.env}"
+
+ config.action_mailer.perform_caching = false
+
+ # Ignore bad email addresses and do not raise email delivery errors.
+ # Set this to true and configure the email server for immediate delivery to raise delivery errors.
+ # config.action_mailer.raise_delivery_errors = false
+
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
+ # the I18n.default_locale when a translation cannot be found).
+ config.i18n.fallbacks = true
+
+ # Send deprecation notices to registered listeners.
+ config.active_support.deprecation = :notify
+
+ # Use default logging formatter so that PID and timestamp are not suppressed.
+ config.log_formatter = ::Logger::Formatter.new
+
+ # Use a different logger for distributed setups.
+ # require 'syslog/logger'
+ # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
+
+ if ENV["RAILS_LOG_TO_STDOUT"].present?
+ logger = ActiveSupport::Logger.new(STDOUT)
+ logger.formatter = config.log_formatter
+ config.logger = ActiveSupport::TaggedLogging.new(logger)
+ end
+
+ # Do not dump schema after migrations.
+ config.active_record.dump_schema_after_migration = false
+end
diff --git a/config/environments/test.rb b/config/environments/test.rb
new file mode 100644
index 0000000000..0a38fd3ce9
--- /dev/null
+++ b/config/environments/test.rb
@@ -0,0 +1,46 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # The test environment is used exclusively to run your application's
+ # test suite. You never need to work with it otherwise. Remember that
+ # your test database is "scratch space" for the test suite and is wiped
+ # and recreated between test runs. Don't rely on the data there!
+ config.cache_classes = true
+
+ # Do not eager load code on boot. This avoids loading your whole application
+ # just for the purpose of running a single test. If you are using a tool that
+ # preloads Rails for running tests, you may have to set it to true.
+ config.eager_load = false
+
+ # Configure public file server for tests with Cache-Control for performance.
+ config.public_file_server.enabled = true
+ config.public_file_server.headers = {
+ 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
+ }
+
+ # Show full error reports and disable caching.
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Raise exceptions instead of rendering exception templates.
+ config.action_dispatch.show_exceptions = false
+
+ # Disable request forgery protection in test environment.
+ config.action_controller.allow_forgery_protection = false
+
+ # Store uploaded files on the local file system in a temporary directory
+ config.active_storage.service = :test
+
+ config.action_mailer.perform_caching = false
+
+ # Tell Action Mailer not to deliver emails to the real world.
+ # The :test delivery method accumulates sent emails in the
+ # ActionMailer::Base.deliveries array.
+ config.action_mailer.delivery_method = :test
+
+ # Print deprecation notices to the stderr.
+ config.active_support.deprecation = :stderr
+
+ # Raises error for missing translations
+ # config.action_view.raise_on_missing_translations = true
+end
diff --git a/config/initializers/action_view.rb b/config/initializers/action_view.rb
new file mode 100644
index 0000000000..142d382f87
--- /dev/null
+++ b/config/initializers/action_view.rb
@@ -0,0 +1 @@
+Rails.application.config.action_view.form_with_generates_remote_forms = false
diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb
new file mode 100644
index 0000000000..89d2efab2b
--- /dev/null
+++ b/config/initializers/application_controller_renderer.rb
@@ -0,0 +1,8 @@
+# Be sure to restart your server when you modify this file.
+
+# ActiveSupport::Reloader.to_prepare do
+# ApplicationController.renderer.defaults.merge!(
+# http_host: 'example.org',
+# https: false
+# )
+# end
diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb
new file mode 100644
index 0000000000..4b828e80cb
--- /dev/null
+++ b/config/initializers/assets.rb
@@ -0,0 +1,14 @@
+# Be sure to restart your server when you modify this file.
+
+# Version of your assets, change this if you want to expire all your assets.
+Rails.application.config.assets.version = '1.0'
+
+# Add additional assets to the asset load path.
+# Rails.application.config.assets.paths << Emoji.images_path
+# Add Yarn node_modules folder to the asset load path.
+Rails.application.config.assets.paths << Rails.root.join('node_modules')
+
+# Precompile additional assets.
+# application.js, application.css, and all non-JS/CSS in the app/assets
+# folder are already added.
+# Rails.application.config.assets.precompile += %w( admin.js admin.css )
diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb
new file mode 100644
index 0000000000..59385cdf37
--- /dev/null
+++ b/config/initializers/backtrace_silencers.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
+# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
+
+# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
+# Rails.backtrace_cleaner.remove_silencers!
diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb
new file mode 100644
index 0000000000..d3bcaa5ec8
--- /dev/null
+++ b/config/initializers/content_security_policy.rb
@@ -0,0 +1,25 @@
+# Be sure to restart your server when you modify this file.
+
+# Define an application-wide content security policy
+# For further information see the following documentation
+# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
+
+# Rails.application.config.content_security_policy do |policy|
+# policy.default_src :self, :https
+# policy.font_src :self, :https, :data
+# policy.img_src :self, :https, :data
+# policy.object_src :none
+# policy.script_src :self, :https
+# policy.style_src :self, :https
+
+# # Specify URI for violation reports
+# # policy.report_uri "/csp-violation-report-endpoint"
+# end
+
+# If you are using UJS then enable automatic nonce generation
+# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
+
+# Report CSP violations to a specified URI
+# For further information see the following documentation:
+# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
+# Rails.application.config.content_security_policy_report_only = true
diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb
new file mode 100644
index 0000000000..5a6a32d371
--- /dev/null
+++ b/config/initializers/cookies_serializer.rb
@@ -0,0 +1,5 @@
+# Be sure to restart your server when you modify this file.
+
+# Specify a serializer for the signed and encrypted cookie jars.
+# Valid options are :json, :marshal, and :hybrid.
+Rails.application.config.action_dispatch.cookies_serializer = :json
diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb
new file mode 100644
index 0000000000..4a994e1e7b
--- /dev/null
+++ b/config/initializers/filter_parameter_logging.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Configure sensitive parameters which will be filtered from the log file.
+Rails.application.config.filter_parameters += [:password]
diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb
new file mode 100644
index 0000000000..ac033bf9dc
--- /dev/null
+++ b/config/initializers/inflections.rb
@@ -0,0 +1,16 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new inflection rules using the following format. Inflections
+# are locale specific, and you may define rules for as many different
+# locales as you wish. All of these examples are active by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.plural /^(ox)$/i, '\1en'
+# inflect.singular /^(ox)en/i, '\1'
+# inflect.irregular 'person', 'people'
+# inflect.uncountable %w( fish sheep )
+# end
+
+# These inflection rules are supported but not enabled by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.acronym 'RESTful'
+# end
diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb
new file mode 100644
index 0000000000..dc1899682b
--- /dev/null
+++ b/config/initializers/mime_types.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new mime types for use in respond_to blocks:
+# Mime::Type.register "text/richtext", :rtf
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
new file mode 100644
index 0000000000..ef8c60c371
--- /dev/null
+++ b/config/initializers/omniauth.rb
@@ -0,0 +1,4 @@
+# config/initializers/omniauth.rb
+Rails.application.config.middleware.use OmniAuth::Builder do
+ provider :github, ENV["GITHUB_CLIENT_ID"], ENV["GITHUB_CLIENT_SECRET"], scope: "user:email"
+end
diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb
new file mode 100644
index 0000000000..bbfc3961bf
--- /dev/null
+++ b/config/initializers/wrap_parameters.rb
@@ -0,0 +1,14 @@
+# Be sure to restart your server when you modify this file.
+
+# This file contains settings for ActionController::ParamsWrapper which
+# is enabled by default.
+
+# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
+ActiveSupport.on_load(:action_controller) do
+ wrap_parameters format: [:json]
+end
+
+# To enable root element in JSON for ActiveRecord objects.
+# ActiveSupport.on_load(:active_record) do
+# self.include_root_in_json = true
+# end
diff --git a/config/locales/en.yml b/config/locales/en.yml
new file mode 100644
index 0000000000..decc5a8573
--- /dev/null
+++ b/config/locales/en.yml
@@ -0,0 +1,33 @@
+# Files in the config/locales directory are used for internationalization
+# and are automatically loaded by Rails. If you want to use locales other
+# than English, add the necessary files in this directory.
+#
+# To use the locales, use `I18n.t`:
+#
+# I18n.t 'hello'
+#
+# In views, this is aliased to just `t`:
+#
+# <%= t('hello') %>
+#
+# To use a different locale, set it with `I18n.locale`:
+#
+# I18n.locale = :es
+#
+# This would use the information in config/locales/es.yml.
+#
+# The following keys must be escaped otherwise they will not be retrieved by
+# the default I18n backend:
+#
+# true, false, on, off, yes, no
+#
+# Instead, surround them with single quotes.
+#
+# en:
+# 'true': 'foo'
+#
+# To learn more, please read the Rails Internationalization guide
+# available at http://guides.rubyonrails.org/i18n.html.
+
+en:
+ hello: "Hello world"
diff --git a/config/puma.rb b/config/puma.rb
new file mode 100644
index 0000000000..a5eccf816b
--- /dev/null
+++ b/config/puma.rb
@@ -0,0 +1,34 @@
+# Puma can serve each request in a thread from an internal thread pool.
+# The `threads` method setting takes two numbers: a minimum and maximum.
+# Any libraries that use thread pools should be configured to match
+# the maximum value specified for Puma. Default is set to 5 threads for minimum
+# and maximum; this matches the default thread size of Active Record.
+#
+threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
+threads threads_count, threads_count
+
+# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
+#
+port ENV.fetch("PORT") { 3000 }
+
+# Specifies the `environment` that Puma will run in.
+#
+environment ENV.fetch("RAILS_ENV") { "development" }
+
+# Specifies the number of `workers` to boot in clustered mode.
+# Workers are forked webserver processes. If using threads and workers together
+# the concurrency of the application would be max `threads` * `workers`.
+# Workers do not work on JRuby or Windows (both of which do not support
+# processes).
+#
+# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
+
+# Use the `preload_app!` method when specifying a `workers` number.
+# This directive tells Puma to first boot the application and load code
+# before forking the application. This takes advantage of Copy On Write
+# process behavior so workers use less memory.
+#
+# preload_app!
+
+# Allow puma to be restarted by `rails restart` command.
+plugin :tmp_restart
diff --git a/config/routes.rb b/config/routes.rb
new file mode 100644
index 0000000000..f591291cb7
--- /dev/null
+++ b/config/routes.rb
@@ -0,0 +1,41 @@
+Rails.application.routes.draw do
+
+
+ root 'products#homepage'
+
+ resources :order_items
+ put '/orders/confirmation', to: 'orders#confirmation', as: 'confirmation'
+ get 'orders/nosnacks', to: 'orders#nosnacks', as: 'nosnacks'
+ get 'orders/:id/finalize', to: 'orders#finalize', as: 'finalize'
+ get 'orders/summary/:id', to: 'orders#summary', as: 'summary'
+ post 'products/:id/order_items', to: 'order_items#create', as: 'add_item'
+ post 'order_items/:id/ship', to: 'order_items#ship', as: 'ship'
+
+ # get 'pages/search', to: 'pages'
+ resources :orders, except: [:create]
+ resources :categories, except: [:index, :edit, :update, :destroy]
+
+ resources :products do
+ resources :order_items, only: [:index, :show, :create, :update]
+ end
+
+ resources :merchants
+ resources :sessions
+
+ get '/home', to: 'products#homepage', as: 'home'
+ # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
+
+ get '/myaccount/orders', to: 'merchants#account_order', as: 'merchant_order'
+
+ get '/myaccount', to: 'merchants#account', as: 'merchant_account'
+
+ get "/auth/:provider/callback", to: "merchants#create", as: 'merchant_callback'
+
+ delete "/logout", to: "merchants#destroy", as: "logout"
+ post "/products/:id", to: "merchants#status_change", as: 'status_change'
+
+ post '/products/:id/reviews', to: 'reviews#create', as: 'create_review'
+
+ get '/bytesquad', to: 'pages#bytesquad', as: 'bytesquad'
+
+end
diff --git a/config/spring.rb b/config/spring.rb
new file mode 100644
index 0000000000..9fa7863f99
--- /dev/null
+++ b/config/spring.rb
@@ -0,0 +1,6 @@
+%w[
+ .ruby-version
+ .rbenv-vars
+ tmp/restart.txt
+ tmp/caching-dev.txt
+].each { |path| Spring.watch(path) }
diff --git a/config/storage.yml b/config/storage.yml
new file mode 100644
index 0000000000..d32f76e8fb
--- /dev/null
+++ b/config/storage.yml
@@ -0,0 +1,34 @@
+test:
+ service: Disk
+ root: <%= Rails.root.join("tmp/storage") %>
+
+local:
+ service: Disk
+ root: <%= Rails.root.join("storage") %>
+
+# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
+# amazon:
+# service: S3
+# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
+# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
+# region: us-east-1
+# bucket: your_own_bucket
+
+# Remember not to checkin your GCS keyfile to a repository
+# google:
+# service: GCS
+# project: your_project
+# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
+# bucket: your_own_bucket
+
+# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
+# microsoft:
+# service: AzureStorage
+# storage_account_name: your_account_name
+# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
+# container: your_container_name
+
+# mirror:
+# service: Mirror
+# primary: local
+# mirrors: [ amazon, google, microsoft ]
diff --git a/db/migrate/20181018194037_create_merchants.rb b/db/migrate/20181018194037_create_merchants.rb
new file mode 100644
index 0000000000..1083bad781
--- /dev/null
+++ b/db/migrate/20181018194037_create_merchants.rb
@@ -0,0 +1,12 @@
+class CreateMerchants < ActiveRecord::Migration[5.2]
+ def change
+ create_table :merchants do |t|
+ t.string :name
+ t.string :email
+ t.integer :uid, null: false
+ t.string :provider, null: false
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20181018195004_create_products.rb b/db/migrate/20181018195004_create_products.rb
new file mode 100644
index 0000000000..8a42301724
--- /dev/null
+++ b/db/migrate/20181018195004_create_products.rb
@@ -0,0 +1,12 @@
+class CreateProducts < ActiveRecord::Migration[5.2]
+ def change
+ create_table :products do |t|
+ t.string :name
+ t.decimal :price
+ t.text :description
+ t.string :img_file
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20181018195153_create_categories.rb b/db/migrate/20181018195153_create_categories.rb
new file mode 100644
index 0000000000..6ccc3914a0
--- /dev/null
+++ b/db/migrate/20181018195153_create_categories.rb
@@ -0,0 +1,9 @@
+class CreateCategories < ActiveRecord::Migration[5.2]
+ def change
+ create_table :categories do |t|
+ t.string :name
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20181018195730_create_orders.rb b/db/migrate/20181018195730_create_orders.rb
new file mode 100644
index 0000000000..d73df43106
--- /dev/null
+++ b/db/migrate/20181018195730_create_orders.rb
@@ -0,0 +1,17 @@
+class CreateOrders < ActiveRecord::Migration[5.2]
+ def change
+ create_table :orders do |t|
+ t.string :email
+ t.string :address
+ t.integer :cc_num
+ t.datetime :order_placed
+ t.string :status
+ t.integer :zip
+ t.string :name
+ t.integer :cvv
+ t.datetime :exp_date
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20181018200332_create_order_items.rb b/db/migrate/20181018200332_create_order_items.rb
new file mode 100644
index 0000000000..cd9db5ef0d
--- /dev/null
+++ b/db/migrate/20181018200332_create_order_items.rb
@@ -0,0 +1,9 @@
+class CreateOrderItems < ActiveRecord::Migration[5.2]
+ def change
+ create_table :order_items do |t|
+ t.integer :quantity
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20181018212022_create_categories_products_join.rb b/db/migrate/20181018212022_create_categories_products_join.rb
new file mode 100644
index 0000000000..7bcfaadab5
--- /dev/null
+++ b/db/migrate/20181018212022_create_categories_products_join.rb
@@ -0,0 +1,8 @@
+class CreateCategoriesProductsJoin < ActiveRecord::Migration[5.2]
+ def change
+ create_table :categories_products do |t|
+ t.belongs_to :category, index: true
+ t.belongs_to :product, index: true
+ end
+ end
+end
diff --git a/db/migrate/20181018213304_add_merchant_id_to_product.rb b/db/migrate/20181018213304_add_merchant_id_to_product.rb
new file mode 100644
index 0000000000..77257b8727
--- /dev/null
+++ b/db/migrate/20181018213304_add_merchant_id_to_product.rb
@@ -0,0 +1,5 @@
+class AddMerchantIdToProduct < ActiveRecord::Migration[5.2]
+ def change
+ add_reference :products, :merchant, foreign_key: true
+ end
+end
diff --git a/db/migrate/20181018213827_add_product_id_to_order_item.rb b/db/migrate/20181018213827_add_product_id_to_order_item.rb
new file mode 100644
index 0000000000..e0448c0a8d
--- /dev/null
+++ b/db/migrate/20181018213827_add_product_id_to_order_item.rb
@@ -0,0 +1,5 @@
+class AddProductIdToOrderItem < ActiveRecord::Migration[5.2]
+ def change
+ add_reference :order_items, :product, foreign_key: true
+ end
+end
diff --git a/db/migrate/20181018214230_add_order_id_to_orderitem.rb b/db/migrate/20181018214230_add_order_id_to_orderitem.rb
new file mode 100644
index 0000000000..f35d7ad394
--- /dev/null
+++ b/db/migrate/20181018214230_add_order_id_to_orderitem.rb
@@ -0,0 +1,5 @@
+class AddOrderIdToOrderitem < ActiveRecord::Migration[5.2]
+ def change
+ add_reference :order_items, :order, foreign_key: true
+ end
+end
diff --git a/db/migrate/20181018234539_add_order_item_id_to_orders.rb b/db/migrate/20181018234539_add_order_item_id_to_orders.rb
new file mode 100644
index 0000000000..813055f124
--- /dev/null
+++ b/db/migrate/20181018234539_add_order_item_id_to_orders.rb
@@ -0,0 +1,5 @@
+class AddOrderItemIdToOrders < ActiveRecord::Migration[5.2]
+ def change
+ add_reference :orders, :order_item, foreign_key: true
+ end
+end
diff --git a/db/migrate/20181020175745_change_order_integer_limit.rb b/db/migrate/20181020175745_change_order_integer_limit.rb
new file mode 100644
index 0000000000..89b2b1cd7e
--- /dev/null
+++ b/db/migrate/20181020175745_change_order_integer_limit.rb
@@ -0,0 +1,5 @@
+class ChangeOrderIntegerLimit < ActiveRecord::Migration[5.2]
+ def change
+ change_column :orders, :cc_num, :integer, :limit => 8
+ end
+end
diff --git a/db/migrate/20181021152304_remove_order_item_column.rb b/db/migrate/20181021152304_remove_order_item_column.rb
new file mode 100644
index 0000000000..2a5bcbfc42
--- /dev/null
+++ b/db/migrate/20181021152304_remove_order_item_column.rb
@@ -0,0 +1,5 @@
+class RemoveOrderItemColumn < ActiveRecord::Migration[5.2]
+ def change
+ remove_column :orders, :order_item_id
+ end
+end
diff --git a/db/migrate/20181021222617_add_inventory_column_to_products.rb b/db/migrate/20181021222617_add_inventory_column_to_products.rb
new file mode 100644
index 0000000000..cb2d9c7c0f
--- /dev/null
+++ b/db/migrate/20181021222617_add_inventory_column_to_products.rb
@@ -0,0 +1,5 @@
+class AddInventoryColumnToProducts < ActiveRecord::Migration[5.2]
+ def change
+ add_column :products, :inventory, :integer
+ end
+end
diff --git a/db/migrate/20181022175055_change_product_and_order_status.rb b/db/migrate/20181022175055_change_product_and_order_status.rb
new file mode 100644
index 0000000000..c991b9dc82
--- /dev/null
+++ b/db/migrate/20181022175055_change_product_and_order_status.rb
@@ -0,0 +1,6 @@
+class ChangeProductAndOrderStatus < ActiveRecord::Migration[5.2]
+ def change
+ add_column :products, :status, :boolean, default: true
+ change_column :orders, :status, :string, default: 'pending'
+ end
+end
diff --git a/db/migrate/20181023003727_create_reviews.rb b/db/migrate/20181023003727_create_reviews.rb
new file mode 100644
index 0000000000..3242b4af7b
--- /dev/null
+++ b/db/migrate/20181023003727_create_reviews.rb
@@ -0,0 +1,10 @@
+class CreateReviews < ActiveRecord::Migration[5.2]
+ def change
+ create_table :reviews do |t|
+ t.integer :rating
+ t.text :description
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20181023003953_add_product_fkey_to_reviews.rb b/db/migrate/20181023003953_add_product_fkey_to_reviews.rb
new file mode 100644
index 0000000000..4e374b3bd7
--- /dev/null
+++ b/db/migrate/20181023003953_add_product_fkey_to_reviews.rb
@@ -0,0 +1,5 @@
+class AddProductFkeyToReviews < ActiveRecord::Migration[5.2]
+ def change
+ add_reference :reviews, :product, foreign_key: true
+ end
+end
diff --git a/db/migrate/20181023004607_change_exp_date_column_to_mmyy.rb b/db/migrate/20181023004607_change_exp_date_column_to_mmyy.rb
new file mode 100644
index 0000000000..f04ba75e21
--- /dev/null
+++ b/db/migrate/20181023004607_change_exp_date_column_to_mmyy.rb
@@ -0,0 +1,9 @@
+class ChangeExpDateColumnToMmyy < ActiveRecord::Migration[5.2]
+ def change
+
+ remove_column :orders, :exp_date
+ add_column :orders, :exp_month, :integer
+ add_column :orders, :exp_year, :integer
+
+ end
+end
diff --git a/db/migrate/20181025220427_add_column_to_order_items.rb b/db/migrate/20181025220427_add_column_to_order_items.rb
new file mode 100644
index 0000000000..65116a0d09
--- /dev/null
+++ b/db/migrate/20181025220427_add_column_to_order_items.rb
@@ -0,0 +1,5 @@
+class AddColumnToOrderItems < ActiveRecord::Migration[5.2]
+ def change
+ add_column :order_items, :status, :boolean, default: false
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
new file mode 100644
index 0000000000..3941f803f1
--- /dev/null
+++ b/db/schema.rb
@@ -0,0 +1,92 @@
+# This file is auto-generated from the current state of the database. Instead
+# of editing this file, please use the migrations feature of Active Record to
+# incrementally modify your database, and then regenerate this schema definition.
+#
+# Note that this schema.rb definition is the authoritative source for your
+# database schema. If you need to create the application database on another
+# system, you should be using db:schema:load, not running all the migrations
+# from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# you'll amass, the slower it'll run and the greater likelihood for issues).
+#
+# It's strongly recommended that you check this file into your version control system.
+
+ActiveRecord::Schema.define(version: 2018_10_25_220427) do
+
+ # These are extensions that must be enabled in order to support this database
+ enable_extension "plpgsql"
+
+ create_table "categories", force: :cascade do |t|
+ t.string "name"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
+ create_table "categories_products", force: :cascade do |t|
+ t.bigint "category_id"
+ t.bigint "product_id"
+ t.index ["category_id"], name: "index_categories_products_on_category_id"
+ t.index ["product_id"], name: "index_categories_products_on_product_id"
+ end
+
+ create_table "merchants", force: :cascade do |t|
+ t.string "name"
+ t.string "email"
+ t.integer "uid", null: false
+ t.string "provider", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
+ create_table "order_items", force: :cascade do |t|
+ t.integer "quantity"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.bigint "product_id"
+ t.bigint "order_id"
+ t.boolean "status", default: false
+ t.index ["order_id"], name: "index_order_items_on_order_id"
+ t.index ["product_id"], name: "index_order_items_on_product_id"
+ end
+
+ create_table "orders", force: :cascade do |t|
+ t.string "email"
+ t.string "address"
+ t.bigint "cc_num"
+ t.datetime "order_placed"
+ t.string "status", default: "pending"
+ t.integer "zip"
+ t.string "name"
+ t.integer "cvv"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.integer "exp_month"
+ t.integer "exp_year"
+ end
+
+ create_table "products", force: :cascade do |t|
+ t.string "name"
+ t.decimal "price"
+ t.text "description"
+ t.string "img_file"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.bigint "merchant_id"
+ t.integer "inventory"
+ t.boolean "status", default: true
+ t.index ["merchant_id"], name: "index_products_on_merchant_id"
+ end
+
+ create_table "reviews", force: :cascade do |t|
+ t.integer "rating"
+ t.text "description"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.bigint "product_id"
+ t.index ["product_id"], name: "index_reviews_on_product_id"
+ end
+
+ add_foreign_key "order_items", "orders"
+ add_foreign_key "order_items", "products"
+ add_foreign_key "products", "merchants"
+ add_foreign_key "reviews", "products"
+end
diff --git a/db/seed_data/categories.csv b/db/seed_data/categories.csv
new file mode 100644
index 0000000000..cc419abd40
--- /dev/null
+++ b/db/seed_data/categories.csv
@@ -0,0 +1,9 @@
+id,name
+1,Mexico
+2,Korea
+3,India
+4,Ukraine
+5,Philippines
+6,Germany
+7,Scotland
+8,Japan
\ No newline at end of file
diff --git a/db/seed_data/merchant.csv b/db/seed_data/merchant.csv
new file mode 100644
index 0000000000..a655f86253
--- /dev/null
+++ b/db/seed_data/merchant.csv
@@ -0,0 +1,9 @@
+id,name,email,uid,provider
+1,Hola,mexico@gmail.com,1,github
+2,Yeoboseyo,korea@gmail.com,2,github
+3,Namaste,india@gmail.com,3,github
+4,Zdrastuyte,ukraine@gmail.com,4,github
+5,Magandang umaga,philippines@gmail.com,5,github
+6,Hallo,germany@gmail.com,6,github
+7,Hello,scotland@gmail.com,7,github
+8,Kon'nichiwa,japan@gmail.com,8,github
\ No newline at end of file
diff --git a/db/seed_data/products.csv b/db/seed_data/products.csv
new file mode 100644
index 0000000000..9e1e38af06
--- /dev/null
+++ b/db/seed_data/products.csv
@@ -0,0 +1,45 @@
+id,name,price,img_file,description,merchant_id,inventory
+1,Salsagheti,2.00,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p261_i1_w640.jpeg,I'm neither salsa nor spaghetti but am really mango candy masquarading as an italian savory snack.,1,5
+2,Takis,2.50,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p179_i4_w640.jpeg,Who you calling tacky?,1,4
+3,Donitas,1.00,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p184_i5_w640.jpeg,Don't need das or dees.,1,5
+4,Lucas Muecas Picante Furia,4.04,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p982_i6_w640.jpeg,Tickly pickly fun on a stick.,1,5
+5,Pica Limon,4.63,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p188_i1_w640.jpeg,Pick a lemon will ya?,1,7
+6,Shrimp Cracker,3.38,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p311_i5_w640.jpeg,Who you callin' shrimp?,2,5
+7,Zzalduck Snack,3.20,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p981_i2_w640.jpeg,Is that really all Duck?,2,6
+8,Ppushu Ppushu,4.01,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p961_i7_w634.jpeg,Poo poo you too.,2,5
+9,White Heim Cookies,1.40,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p491_i7_w640.jpeg,Vanilla and hazelnut wafer cookies,2,5
+10,Ginsing Jellies,1.10,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p611_i3_w640.jpeg,Sing Gin!,2,5
+11,"Parle-G Biscuits
+",1.21,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p829_i3_w640.jpeg,risk it like biscit!,3,5
+12,Kurkure Chilli Chatka,1.31,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p995_i3_w640.jpeg,spicy chips,3,5
+13,Parle Kaccha Mango Bite,4.48,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p1031_i1_w458.jpeg,spicy green mangoes,3,5
+14,Hide and Seek Fab Orange,3.63,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p841_i12_w640.jpeg,Now you see me now you don't!,3,10
+15,Good Day Butter Cookies,6.82,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p834_i9_w640.jpeg,Can't go wrong when ur having a Good Day!,3,5
+16,Roshen Chamomile,13.07,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p589_i8_w600.jpeg,Cheer up and eat a daisy!,4,5
+17,Roshen Minky Binky Grape,3.70,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p910_i7_w640.jpeg,Blinky Blink with Joy,4,5
+18,Roshen Choconilla,1.95,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p532_i3_w600.jpeg,Hard Chocolate Candy Milk Chocolate Center,4,5
+19,Roshen Leshchina,1.62,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p588_i10_w549.jpeg,Praline with Chopped Hazelnuts Coated in Chocolate,4,5
+20,Nougat Roshen Chocolate,4.04,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p503_i6_w600.jpeg,Chocolate Nougat Glazed w/ Chocolate Icing,4,5
+21,Boy Bawang Cornick Garlic Flavor,6.06,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p288_i3_w640.jpeg,Oh BOY it's a garlic punch in the mouth!,5,9
+22,Ribbed Cracklings in Old Fashioned Salt & Vinegar,1.41,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p287_i1_w640.jpeg,Salt and vinegar flavored chips,5,5
+23,Skyflakes Tsokolate,3.21,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p236_i2_w640.jpeg,Crackers with chocolate filling ,5,5
+24,ChocNut ,1.78,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p757_i3_w504.jpeg,Peanut butter flavored mini milk chocolate bars,5,5
+25,Golden Sweet Corn,4.41,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p459_i3_w550.jpeg,"Imitation cheese balls, but like corn flavored",5,1
+26,Dried Guava Balls,3.52,https://www.asianfoodgrocer.com/media/catalog/product/cache/1/image/800x800/9df78eab33525d08d6e5fb8d27136e95/p/h/philippine_dried_guava_copy_1.jpg,Hurry and guava ball before it's too late!,5,2
+27,Pandan Cake,8.52,http://parintele.info/wp-content/uploads/regent-pandan-cake-regent-cakes.jpeg,Pandan flavored spongy cake,5,8
+28,Salzige Heringe,4.56,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p336_i3_w640.jpeg,To satisfy your cravings for salted herring and licorice. Yum!,6,5
+29,Duplo,1.00,https://www.austriansupermarket.com/media/catalog/product/cache/2/image/800x800/9df78eab33525d08d6e5fb8d27136e95/2/8/2884_6.jpg,Buy me now... don't wafer it to sell out! ,6,5
+30,Toffifee,1.54,https://i.ebayimg.com/images/g/SFIAAOSwKBVbUbfa/s-l300.jpg,toffifee or covfefe?,6,5
+31,Haribo Katinchen,3.20,https://www.economycandy.com/wp-content/uploads/2016/03/German-Haribo-Katinchen-Black-Licorice-Cats.jpg,I don't know about you but these aren't haribo. ,6,5
+32,Hanuta,2.73,https://images-na.ssl-images-amazon.com/images/I/51g57PJ7CUL._SY355_.jpg,I can't wafer this chocolate to get in my belly!!,6,5
+33,Tunnock's Tea Cakes,2.13,https://myfreelancelunch.files.wordpress.com/2010/07/tunnocksteacakes.jpg,Round shortbread biscuit topped with fluffy meringue and coated in a velvety layer of dark or milk chocolate,7,5
+34,Tunnock's Caramel Wafer Biscuit,2.03,https://www.englishteastore.com/media/catalog/product/cache/6/image/9df78eab33525d08d6e5fb8d27136e95/f/c/fcc_tun_cmwaf_-00_tunnocks-caramel-wafer-2016_1.jpg,Layers of buttery caramel and wafers coated in chocolate,7,5
+35,Scottish Shortbread Cookie,4.08,http://americanscottishfoundation.com/magazine/wp-content/uploads/2016/04/ALAMY-Shortbread-487643.jpg,Classic buttery shortbread cookie,7,5
+36,Deep-Fried Mars Bar,4.39,https://noseychef.com/wp-content/uploads/2017/12/mars.jpg,Nougat and almond filled chocolate bar battered and deep fried till it's slighty molten on the inside and crunchy on the out,7,5
+37,Tablet,1.90,https://bakingwithgranny.co.uk/wp-content/uploads/2017/05/tablet2.jpg,Apple is not usually involved in the making of this tablet,7,5
+38,Arare,1.06,"https://assets.epicurious.com/photos/59377547f692c10ac01b0006/6:4/w_288,h_192,c_limit/shutterstock_355278341.jpg","You'll be arare-in to go with these crunchy rice flour flavored with soy sauce. Accented with nori seaweed, peanuts or wasabi",8,5
+39,Want want Senbei Rice Cracker,3.81,https://i5.walmartimages.ca/images/Large/636/245/636245.jpg,"Crispy, wafer-like rice flour crackers. Addictive! You'll want want more!",8,5
+40,Meiji Karl Cheese Puffs,4.01,https://www.worldofsnacks.com/uploads/1/3/1/2/13127180/s643667121527569464_p594_i6_w456.jpeg,Not corny at all,8,5
+41,Pocky,2.96,"https://assets.epicurious.com/photos/5937e952f692c10ac01b0008/6:4/w_620,h_413,c_limit/shutterstock_540306985.jpg",pretzel sticks dipped in an assortment of delicious flavors. May snap if stored in pockets.,8,5
+42,Matcha Kit Kat,1.47,"https://assets.epicurious.com/photos/5937778d457cd54e371ee156/6:4/w_620,h_413,c_limit/shutterstock_366302480.jpg",Got some OG Kit Kat? Mix and Matcha with these layers of thin wafer wrapped in matcha flavored chocolate,8,2
+43,Mochi,2.68,"https://assets.epicurious.com/photos/5937ea1d457cd54e371ee158/6:4/w_620,h_413,c_limit/shutterstock_131798075.jpg",You'll want mochi more of these soft and chewy rice cakes filled with an assortment of flavors,8,3
diff --git a/db/seed_data/reviews.csv b/db/seed_data/reviews.csv
new file mode 100644
index 0000000000..137cf6d36d
--- /dev/null
+++ b/db/seed_data/reviews.csv
@@ -0,0 +1,40 @@
+id,rating,description,product_id
+1,5,Incredible!,1
+2,4,Wow. I'd buy this again.,2
+3,3,Good,3
+4,3,Good,4
+5,1,Ugggg.....,5
+6,5,Incredible!,6
+7,5,Incredible!,7
+8,4,Wow. I'd buy this again.,8
+9,5,Incredible!,9
+10,4,Wow. I'd buy this again.,10
+11,3,Good,11
+12,3,Good,12
+13,1,Ugggg.....,13
+14,5,Incredible!,14
+15,5,Incredible!,15
+16,4,Wow. I'd buy this again.,16
+17,5,Incredible!,17
+18,4,Wow. I'd buy this again.,18
+19,3,Good,19
+20,3,Good,20
+21,1,Ugggg.....,21
+22,5,Incredible!,22
+23,5,Incredible!,23
+24,4,Wow. I'd buy this again.,24
+25,5,Incredible!,25
+26,4,Wow. I'd buy this again.,26
+27,3,Good,27
+28,3,Good,28
+29,1,Ugggg.....,29
+30,5,Incredible!,30
+31,5,Incredible!,31
+32,4,Wow. I'd buy this again.,32
+33,5,Incredible!,33
+34,4,Wow. I'd buy this again.,34
+35,3,Good,35
+36,3,Good,36
+37,1,Ugggg.....,37
+38,4,Wow. I'd buy this again.,38
+39,4,Wow. I'd buy this again.,39
diff --git a/db/seeds.rb b/db/seeds.rb
new file mode 100644
index 0000000000..030bfc548d
--- /dev/null
+++ b/db/seeds.rb
@@ -0,0 +1,131 @@
+# This file should contain all the record creation needed to seed the database with its default values.
+# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
+#
+# Examples:
+#
+# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
+# Character.create(name: 'Luke', movie: movies.first)
+
+require 'csv'
+
+MERCHANT_FILE = Rails.root.join('db', 'seed_data', 'merchant.csv')
+puts "Loading raw merchant data from #{MERCHANT_FILE}"
+
+merchant_failures = []
+CSV.foreach(MERCHANT_FILE, :headers => true) do |row|
+ merchant = Merchant.new
+ merchant.id = row['id']
+ merchant.name = row['name']
+ merchant.email = row['email']
+ merchant.uid = row['uid']
+ merchant.provider = row['provider']
+ successful = merchant.save
+ if !successful
+ merchant_failures << merchant
+ puts "Failed to save merchant: #{merchant.inspect}"
+ else
+ puts "Created merchant: #{merchant.inspect}"
+ end
+end
+
+puts "Added #{Merchant.count} merchant records"
+puts "#{merchant_failures.length} merchants failed to save"
+
+
+CATEGORY_FILE = Rails.root.join('db', 'seed_data', 'categories.csv')
+puts "Loading raw category data from #{CATEGORY_FILE}"
+
+category_failures = []
+CSV.foreach(CATEGORY_FILE, :headers => true) do |row|
+ category = Category.new
+ category.id = row['id']
+ category.name = row['name']
+ successful = category.save
+ if !successful
+ category_failures << category
+ puts "Failed to save category: #{category.inspect}"
+ else
+ puts "Created category: #{category.inspect}"
+ end
+end
+
+puts "Added #{Category.count} category records"
+puts "#{category_failures.length} categories failed to save"
+
+
+
+PRODUCT_FILE = Rails.root.join('db', 'seed_data', 'products.csv')
+puts "Loading raw product data from #{PRODUCT_FILE}"
+
+product_failures = []
+CSV.foreach(PRODUCT_FILE, :headers => true) do |row|
+ product = Product.new
+ product.id = row['id']
+ product.name = row['name']
+ product.price = row['price']
+ product.description = row['description']
+ product.img_file= row['img_file']
+ product.merchant_id = row['merchant_id']
+ product.inventory = row['inventory']
+ successful = product.save
+ if !successful
+ product_failures << product
+ puts "Failed to save product: #{product.inspect}"
+ else
+ puts "Created product: #{product.inspect}"
+ end
+end
+
+puts "Added #{Product.count} product records"
+puts "#{product_failures.length} products failed to save"
+
+categories = [1,2,3,4,5,6,7,8]
+categories.each do |i|
+ category = Category.find(i)
+ products = Product.where(merchant_id: i)
+
+ products.each do |product|
+ category.products << product
+ end
+ successful = category.save
+
+ if !successful
+ puts "category#{i} products failed to save product."
+ else
+ puts "category#{i} products successfully saved."
+ end
+
+end
+
+REVIEW_FILE = Rails.root.join('db', 'seed_data', 'reviews.csv')
+puts "Loading raw review data from #{REVIEW_FILE}"
+
+review_failures = []
+CSV.foreach(REVIEW_FILE, :headers => true) do |row|
+ review = Review.new
+ review.id = row['id']
+ review.rating = row['rating']
+ review.description = row['description']
+ review.product_id = row['product_id']
+ successful = review.save
+ if !successful
+ review_failures << review
+ puts "Failed to save review: #{review.inspect}"
+ else
+ puts "Created review: #{review.inspect}"
+ end
+end
+
+puts "Added #{Review.count} review records"
+puts "#{review_failures.length} review failed to save"
+
+# Since we set the primary key (the ID) manually on each of the
+# tables, we've got to tell postgres to reload the latest ID
+# values. Otherwise when we create a new record it will try
+# to start at ID 1, which will be a conflict.
+puts "Manually resetting PK sequence on each table"
+ActiveRecord::Base.connection.tables.each do |t|
+ ActiveRecord::Base.connection.reset_pk_sequence!(t)
+end
+
+puts "done"
diff --git a/lib/assets/.keep b/lib/assets/.keep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/lib/tasks/.keep b/lib/tasks/.keep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/log/.keep b/log/.keep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/package.json b/package.json
new file mode 100644
index 0000000000..f874acf437
--- /dev/null
+++ b/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "betsy",
+ "private": true,
+ "dependencies": {}
+}
diff --git a/public/404.html b/public/404.html
new file mode 100644
index 0000000000..2be3af26fc
--- /dev/null
+++ b/public/404.html
@@ -0,0 +1,67 @@
+
+
+
+ The page you were looking for doesn't exist (404)
+
+
+
+
+
+
+
+
+
The page you were looking for doesn't exist.
+
You may have mistyped the address or the page may have moved.
+
+
If you are the application owner check the logs for more information.
+
+
+
diff --git a/public/422.html b/public/422.html
new file mode 100644
index 0000000000..c08eac0d1d
--- /dev/null
+++ b/public/422.html
@@ -0,0 +1,67 @@
+
+
+
+ The change you wanted was rejected (422)
+
+
+
+
+
+
+
+
+
The change you wanted was rejected.
+
Maybe you tried to change something you didn't have access to.
+
+
If you are the application owner check the logs for more information.
+
+
+
diff --git a/public/500.html b/public/500.html
new file mode 100644
index 0000000000..78a030af22
--- /dev/null
+++ b/public/500.html
@@ -0,0 +1,66 @@
+
+
+
+ We're sorry, but something went wrong (500)
+
+
+
+
+
+
+
+
+
We're sorry, but something went wrong.
+
+
If you are the application owner check the logs for more information.
+
+
+
diff --git a/public/apple-touch-icon-precomposed.png b/public/apple-touch-icon-precomposed.png
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 0000000000..37b576a4a0
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1 @@
+# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
diff --git a/storage/.keep b/storage/.keep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb
new file mode 100644
index 0000000000..d19212abd5
--- /dev/null
+++ b/test/application_system_test_case.rb
@@ -0,0 +1,5 @@
+require "test_helper"
+
+class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
+ driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
+end
diff --git a/test/controllers/.keep b/test/controllers/.keep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/test/controllers/categories_controller_test.rb b/test/controllers/categories_controller_test.rb
new file mode 100644
index 0000000000..d5d455f4be
--- /dev/null
+++ b/test/controllers/categories_controller_test.rb
@@ -0,0 +1,76 @@
+require "test_helper"
+
+describe CategoriesController do
+
+ let(:bad_category_id) { Category.first.destroy.id }
+
+ describe "Logged in merchants" do
+
+ before do
+ perform_login(merchants(:bake_off))
+ end
+
+ describe "access for logged in merchants" do
+
+ it "logged in merchants can access the show page for a category" do
+ category_id = Category.first.id
+ get category_path(category_id)
+ must_respond_with :success
+ end
+
+ it "logged in merchants can access the new category form" do
+ get new_category_path
+ must_respond_with :success
+ end
+ end
+
+ describe "update-ability for logged-in merchants" do
+ describe "create" do
+ it "creates a category with valid data" do
+
+ category_data = {
+ category: {
+ name: "Fuzzy bunnies"
+ }
+ }
+
+ expect {
+ post categories_path, params: category_data
+ }.must_change('Category.count', +1)
+
+ must_redirect_to category_path(Category.last)
+ end
+
+ it "will not create a category, given incomplete data" do
+
+ category_data = {
+ category: {
+ name: nil
+ }
+ }
+
+ expect {
+ post categories_path, params: category_data
+ }.wont_change ('Category.count')
+
+ end
+ end
+ end
+ end
+
+ describe "Guest users" do
+ describe "access for guest users" do
+ it "guest users can access the show page for a category" do
+ category_id = Category.first.id
+ get category_path(category_id)
+ must_respond_with :success
+ end
+
+ it "guest users cannot access the new category form" do
+ get new_category_path
+ must_redirect_to root_path
+ flash[:status].must_equal :failure
+ end
+ end
+ end
+end
diff --git a/test/controllers/merchants_controller_test.rb b/test/controllers/merchants_controller_test.rb
new file mode 100644
index 0000000000..6f11768e0f
--- /dev/null
+++ b/test/controllers/merchants_controller_test.rb
@@ -0,0 +1,200 @@
+require "test_helper"
+
+describe MerchantsController do
+
+ it "can successfully log in with github as an existing merchant" do
+ iron_chef = merchants(:iron_chef)
+
+ perform_login(iron_chef)
+
+ must_redirect_to root_path
+ expect(session[:merchant_id]).must_equal iron_chef.id
+ # expect(flash[:status]).must_equal "Logged in as returning merchant #{merchant.name}"
+ end
+
+ it "can successfully log in with github as new merchant" do
+ start_count = Merchant.count
+ merchant = Merchant.new(provider: "github", uid: 293, name: "New Merchant", email: "new123@gmail.com")
+
+ perform_login(merchant)
+
+ must_redirect_to root_path
+
+ Merchant.count.must_equal start_count + 1
+
+ session[:merchant_id].must_equal Merchant.last.id
+ end
+
+
+ it "does not create a new merchant when logging in with a invalid data" do
+ start_count = Merchant.count
+
+ invalid_new_merchant = Merchant.new(name: nil, email: nil)
+
+ expect(invalid_new_merchant.valid?).must_equal false
+
+ perform_login(invalid_new_merchant)
+
+
+ must_redirect_to root_path
+ expect( session[:merchant_id] ).must_equal nil
+ expect( Merchant.count ).must_equal start_count
+ end
+
+
+ describe "Logged in merchant" do
+
+ before do
+ @hells = merchants(:hells)
+ perform_login(@hells)
+ end
+
+
+ it "signed in merchant can succesfully log out" do
+ id = @hells.id
+
+ delete merchant_path(id)
+
+ expect(session[:merchant_id]).must_equal nil
+ expect(flash[:success]).must_equal "Successfully logged out!"
+ must_redirect_to root_path
+ end
+
+
+ it "should respond with not found for non-exsiting merchant" do
+
+ random_non_existant_id = 333
+ get merchant_path(random_non_existant_id)
+
+ must_respond_with :missing
+ end
+
+ describe "status change" do
+
+ it "logged in merchant can change their product status from activate to inactivate" do
+
+ #get a product
+ thyme = products(:thyme)
+ #route with the id
+ post status_change_path(thyme.id)
+
+ thyme.reload
+
+ expect(thyme.status).must_equal false
+
+ #where it goes next
+ must_redirect_to root_path
+ end
+
+
+ it "if logged in merchants try to change a product status that doesn't exist." do
+ product = Product.first.id + 1
+
+
+ post status_change_path(product)
+ must_respond_with :not_found
+
+ end
+
+ it "if logged in merchants status change doesn't work." do
+ product = Product.first
+ product.status = "fake"
+
+
+ post status_change_path(product)
+ expect(product.status).must_equal true
+
+ end
+
+
+
+
+ # it "logged in merchant can not change the product status of another merchants" do
+ #
+ # delete merchant_path(@hells.id)
+ #
+ # @bake_off = merchants(:bake_off)
+ # perform_login(@bake_off)
+ #
+ # get merchant_account_path(@hells.id)
+ #
+ # must_respond_with :error
+ #
+ # end
+ end
+
+ describe "Merchant Add Products" do
+ it "logged in merchant can successfully add a product" do
+
+ get new_product_path
+
+ must_respond_with :success
+ end
+
+ it "Not logged in merchant can not add a product" do
+
+ delete merchant_path(@hells.id)
+
+ must_redirect_to root_path
+
+ get new_product_path
+
+ flash[:status].must_equal :failure
+ flash[:result_text].must_equal "Sign in as a merchant to access this page."
+ must_respond_with :redirect
+
+ end
+ end
+
+ describe "Merchant can ADD category" do
+ it "logged in merchant can successfully add a category" do
+
+ get new_category_path
+ must_respond_with :success
+ end
+
+ it "A non logged in merchant can NOT add a category" do
+
+ delete merchant_path(@hells.id)
+
+ get new_category_path
+
+ flash[:status].must_equal :failure
+ flash[:result_text].must_equal "Sign in as a merchant to access this page."
+ must_respond_with :redirect
+ end
+
+ end
+
+
+ describe "Merchant Account Order Page" do
+
+
+ it "logged in merchant can view their account orders" do
+ get merchant_order_path
+ must_respond_with :success
+ end
+
+ it "A Not logged in merchant can Not view their account orders" do
+ delete merchant_path(@hells.id)
+
+ get merchant_order_path
+ must_respond_with :not_found
+ end
+ end
+
+ it "logged in merchant can view their myaccount page" do
+ get merchant_account_path
+ must_respond_with :success
+ end
+
+
+ it "Not logged in merchant can Not view their myaccount page" do
+ delete merchant_path(@hells.id)
+ get merchant_account_path
+ must_respond_with :not_found
+ end
+
+ end
+
+end
diff --git a/test/controllers/order_items_controller_test.rb b/test/controllers/order_items_controller_test.rb
new file mode 100644
index 0000000000..dccc1cd00b
--- /dev/null
+++ b/test/controllers/order_items_controller_test.rb
@@ -0,0 +1,277 @@
+require "test_helper"
+
+describe OrderItemsController do
+
+ describe 'create action' do
+ let(:order) { Order.new }
+ let(:existing_product) { Product.first }
+
+ let(:order_item_data) {
+ {
+ id: existing_product.id,
+
+ order_item: {
+ quantity: 2,
+ product_id: existing_product.id
+ }
+ }
+ }
+
+ let(:order_item_data_2) {
+ {
+ id: Product.last.id,
+
+ order_item: {
+ quantity: 2
+ }
+ }
+ }
+
+ it 'creates an order_item and order if cart is empty' do
+ order_item_test = OrderItem.new(order_item_data[:order_item])
+
+
+ order_item_test.wont_be :valid?, "Order data was valid. Engineer, please fix this test."
+
+ old_orders_count = Order.count
+ old_orderItems_count = OrderItem.count
+
+ post order_items_path, params: order_item_data
+
+ expect(Order.count).must_equal old_orders_count + 1
+ expect(OrderItem.count).must_equal old_orderItems_count + 1
+
+ must_redirect_to order_path(Order.last)
+
+ expect(OrderItem.last.quantity).must_equal order_item_data[:order_item][:quantity]
+ expect(OrderItem.last.order_id).must_equal Order.last.id
+ end
+
+ it 'creates an order_item if cart is not empty, associates it with the in-progress order and does not create a new order' do
+
+ post order_items_path, params: order_item_data
+ cart = Order.last
+ old_orders_count = Order.count
+
+ expect {
+ post order_items_path, params: order_item_data_2
+ }.must_change('OrderItem.count', +1)
+
+ must_redirect_to order_path(Order.last)
+
+ expect(Order.count).must_equal old_orders_count
+ expect(OrderItem.last.order_id).must_equal cart.id
+
+ end
+
+ it 'returns bad request if given invalid data and cart exists' do
+
+ post order_items_path, params: order_item_data
+ cart = Order.last
+ old_orders_count = Order.count
+
+ order_item_data_2[:order_item][:quantity] = 0
+
+ expect {
+ post order_items_path, params: order_item_data_2
+ }.wont_change('OrderItem.count')
+
+ must_respond_with 302
+ expect(flash[:messages]).must_include :quantity
+
+ end
+ end
+
+ describe 'update action' do
+ let(:order) { Order.new }
+ let(:existing_product) { Product.first }
+
+ let(:order_item_data) {
+ {
+ id: existing_product.id,
+
+ order_item: {
+ quantity: 2,
+ product_id: existing_product.id
+ }
+ }
+ }
+
+ let(:order_item_setup) {
+ post order_items_path, params: order_item_data
+ }
+
+ let(:old_orders_count) { Order.count }
+ let(:old_orderItems_count) { OrderItem.count }
+ let(:item) { OrderItem.last }
+ let(:max_quantity) { item.product.inventory }
+
+ it 'successfully updates an order_item with valid input and redirects to the shopping cart' do
+ order_item_setup
+
+ order_item_data[:order_item][:quantity] = max_quantity
+
+ put order_item_path(item), params: order_item_data
+
+ must_redirect_to order_path(item.order)
+
+ expect(Order.count).must_equal old_orders_count
+ expect(OrderItem.count).must_equal old_orderItems_count
+ expect(OrderItem.last.quantity).must_equal max_quantity
+ expect(flash[:result_text]).must_include "Successfully updated order for # #{item.product.name}."
+ end
+
+ it 'does not update an order_item with invalid data and redirects back to last page' do
+ order_item_setup
+
+ old_quantity = order_item_data[:order_item][:quantity]
+ order_item_data[:order_item][:quantity] = max_quantity + 1
+
+ put order_item_path(item), params: order_item_data
+
+ must_respond_with 302
+
+ expect(Order.count).must_equal old_orders_count
+ expect(OrderItem.count).must_equal old_orderItems_count
+ expect(OrderItem.last.quantity).must_equal old_quantity
+ expect(flash[:result_text]).must_include "Something went wrong: Could not update order ##{item.id}"
+ expect(flash[:messages]).must_include :quantity
+ end
+
+ it 'responds with bad_request if attempting to update an order_item that does not belong to the cart of the current user' do
+ order_item_setup
+
+ # Arrange: First OrderItem is not associated with current user
+ item = OrderItem.first
+
+ put order_item_path(item), params: order_item_data
+
+ must_respond_with :bad_request
+ end
+
+ it 'responds with bad_request if attempting to update an order_item when the cart is empty' do
+ # Arrange: First OrderItem is not associated with current user
+ item = OrderItem.first
+
+ put order_item_path(item), params: order_item_data
+
+ must_respond_with :bad_request
+ end
+ end
+
+ describe 'destroy action' do
+
+ let(:order) { Order.new }
+ let(:existing_product) { Product.first }
+
+ let(:order_item_data) {
+ {
+ id: existing_product.id,
+
+ order_item: {
+ quantity: 2,
+ product_id: existing_product.id
+ }
+ }
+ }
+
+ let(:order_item_setup) {
+ post order_items_path, params: order_item_data
+ }
+
+ let(:old_orders_count) { Order.count }
+ let(:old_orderItems_count) { OrderItem.count }
+ let(:item) { OrderItem.last }
+ let(:max_quantity) { item.product.inventory }
+
+ it "successfully destroys an order item that is in the user's cart" do
+ order_item_setup
+ cart = item.order
+
+ expect{
+ delete order_item_path(item.id)
+ }.must_change('OrderItem.count', -1)
+
+ must_redirect_to order_path(cart)
+
+ end
+
+ it "responds with 404 not_found if order item does not exist" do
+ order_item_setup
+ delete order_item_path(item.id)
+
+ expect{
+ delete order_item_path(item.id)
+ }.wont_change('OrderItem.count')
+
+ must_respond_with :bad_request
+ end
+
+ it "responds with bad_request if order item does not belong in current cart" do
+ order_item_setup
+ cart = item.order
+
+ expect{
+ delete order_item_path(OrderItem.first)
+ }.wont_change('OrderItem.count')
+
+ must_respond_with :bad_request
+
+ end
+
+ it "responds with bad_request if cart is empty" do
+ expect{
+ delete order_item_path(OrderItem.first)
+ }.wont_change('OrderItem.count')
+
+ must_respond_with :bad_request
+ end
+ end
+
+ describe 'ship action' do
+
+ let(:order) { Order.new }
+ let(:existing_product) { Product.first }
+
+ let(:order_item_data) {
+ {
+ id: existing_product.id,
+
+ order_item: {
+ quantity: 2,
+ product_id: existing_product.id
+ }
+ }
+ }
+
+ let(:order_item_setup) {
+ post order_items_path, params: order_item_data
+ }
+
+ let(:old_orders_count) { Order.count }
+ let(:old_orderItems_count) { OrderItem.count }
+ let(:item) { OrderItem.last }
+ let(:max_quantity) { item.product.inventory }
+
+ it 'changes the status of an order from shipped to not shipped and vice versa' do
+
+ item = OrderItem.first
+ original_status = item.status
+
+ post ship_path(item.id)
+
+ must_respond_with 302
+ item = OrderItem.find(item.id)
+ expect(item.status).must_equal !original_status
+
+ post ship_path(item.id)
+
+ must_respond_with 302
+ item = OrderItem.find(item.id)
+ expect(item.status).must_equal original_status
+
+ end
+
+ end
+
+end
diff --git a/test/controllers/orders_controller_test.rb b/test/controllers/orders_controller_test.rb
new file mode 100644
index 0000000000..6342bfcc63
--- /dev/null
+++ b/test/controllers/orders_controller_test.rb
@@ -0,0 +1,633 @@
+require "test_helper"
+
+describe OrdersController do
+
+ let(:search_data) {
+ {
+ search_id: Order.first.id,
+ search_email: Order.first.email
+ }
+ }
+
+
+ describe 'index action' do
+ it 'sets @orders to nil given no search parameters' do
+
+ get orders_path, params: nil
+
+ must_respond_with 200
+ end
+
+ it 'sets @orders to nil given blank search parameters' do
+ search_data[:search_id] = ""
+ search_data[:search_email] = ""
+
+ get orders_path, params: search_data
+
+ must_respond_with 200
+ end
+
+ it 'sets @orders to return the specific Order instances that meet the search criteria and sets session[:old_order] to track the searched id ' do
+
+ get orders_path, params: search_data
+
+ must_respond_with 200
+
+ controller.session[:old_order].must_equal search_data[:search_id].to_s
+ end
+
+ end
+
+ describe 'show action' do
+
+ let(:existing_order) {Order.first}
+
+ let(:order_item_data) {
+ {
+ id: Product.first.id,
+ order_item: {
+ quantity: 2,
+ }
+ }
+ }
+
+ it "succeeds for an extant order ID if you are the client currently adding to that order" do
+ #Arrange: THIS user makes order
+ post order_items_path, params: order_item_data
+ order = Order.last
+ must_redirect_to order_path(order)
+
+ get order_path(order.id)
+
+ must_respond_with :success
+ end
+
+ it 'renders nosnacks and responds with 404 not found if given a nonexistent order' do
+ old_id = existing_order.id
+ existing_order.order_items.each do |items|
+ items.destroy
+ end
+ existing_order.destroy
+
+ get order_path(existing_order)
+
+ must_respond_with 400
+ end
+ end
+
+ describe 'edit action' do
+
+ # let(:order) { Order.first }
+
+ let(:order_item_data) {
+ {
+ id: Product.first.id,
+ order_item: {
+ quantity: 2,
+ }
+ }
+ }
+
+ let(:order_exists) { post order_items_path, params: order_item_data }
+
+ it "succeeds for an extant order ID when user is the owner of that order/cart" do
+
+ order_exists
+ get edit_order_path(Order.last)
+
+ must_respond_with :success
+ end
+
+ it "renders bad_request for an extant order ID when user is not the owner of that order/cart" do
+
+ order_exists
+ get edit_order_path(Order.first)
+
+ must_respond_with :bad_request
+ end
+
+ it "renders bad_request if cart is empty" do
+
+ get edit_order_path(Order.first)
+
+ must_respond_with :bad_request
+ end
+
+ end
+
+ # TODO: this is verrrry similar to describe validations on update
+ describe 'update' do
+
+
+ let(:order_data) {
+ {
+ order: {
+ name: 'Sherlock Holmes',
+ email: 'smart@ssdetective.com',
+ address: '221B Baker St, London',
+ cc_num: 1234567812345678,
+ cvv: 123,
+ exp_month: 12,
+ exp_year: Date.today.year + 1,
+ zip: 43770
+ }
+ }
+ }
+
+ let(:existing_id) { Order.first.id }
+
+ it "successfully updates a cart with properly formatted and completed fields: name, email, address, zip, cvv, exp_month, exp_year, and cc_num; changes the status to 'awaiting confirmation'; and redirects to the finalize page" do
+
+ expect {
+ put order_path(existing_id), params: order_data
+ }.wont_change('Order.count')
+
+ must_redirect_to finalize_path(existing_id)
+
+ order = Order.find_by(id: existing_id)
+ expect(order.status).must_equal 'awaiting confirmation'
+
+ expect(order.name).must_equal order_data[:order][:name]
+ expect(order.email).must_equal order_data[:order][:email]
+ expect(order.address).must_equal order_data[:order][:address]
+ expect(order.cc_num).must_equal order_data[:order][:cc_num]
+ expect(order.cvv).must_equal order_data[:order][:cvv]
+ expect(order.exp_month).must_equal order_data[:order][:exp_month]
+ expect(order.exp_year).must_equal order_data[:order][:exp_year]
+ expect(order.zip).must_equal order_data[:order][:zip]
+
+ end
+
+ end
+
+ describe 'finalize' do
+
+ let(:order_data) {
+ {
+ order: {
+ name: 'Sherlock Holmes',
+ email: 'smart@ssdetective.com',
+ address: '221B Baker St, London',
+ cc_num: 1234567812345678,
+ cvv: 123,
+ exp_month: 12,
+ exp_year: Date.today.year + 1,
+ zip: 43770
+ }
+ }
+ }
+
+ let(:order_item_data) {
+ {
+ id: Product.first.id,
+ order_item: {
+ quantity: 2,
+ }
+ }
+ }
+
+ it 'succeeds if cart exists and buyer is the owner of the cart' do
+ #Arrange: THIS user makes order
+ post order_items_path, params: order_item_data
+ must_redirect_to order_path(Order.last)
+
+ #Arrange: edit and update order with buyer info
+ order = Order.last
+ put order_path(order), params: order_data
+ must_redirect_to finalize_path(Order.last)
+
+ #Arrange: get to finalization page
+ get finalize_path(order)
+
+ must_respond_with :success
+
+ end
+
+ it 'respond with bad_request if cart exists and buyer is not the owner of this cart' do
+ get finalize_path(Order.last)
+
+ expect(flash[:result_text]).must_include 'Cannot finalize nonexistent order'
+ must_respond_with :bad_request
+
+ end
+
+ it 'responds with bad request if the cart is empty' do
+ @cart.must_be_nil
+
+ get finalize_path(Order.last)
+
+ expect(flash[:result_text]).must_include 'Cannot finalize nonexistent order'
+ must_respond_with :bad_request
+
+ end
+ end
+
+ describe 'confirmation' do
+
+ let(:order_data) {
+ {
+ order: {
+ name: 'Sherlock Holmes',
+ email: 'smart@ssdetective.com',
+ address: '221B Baker St, London',
+ cc_num: 1234567812345678,
+ cvv: 123,
+ exp_month: 12,
+ exp_year: Date.today.year + 1,
+ zip: 43770
+ }
+ }
+ }
+
+ let(:order_item_data) {
+ {
+ id: Product.first.id,
+ order_item: {
+ quantity: 2,
+ }
+ }
+ }
+
+ it 'succeeds if cart exists and empties the cart afterwards' do
+ #Arrange: THIS user makes order
+ post order_items_path, params: order_item_data
+ must_redirect_to order_path(Order.last)
+
+ #Arrange: edit and update order with buyer info
+ order = Order.last
+ put order_path(order), params: order_data
+ must_redirect_to finalize_path(Order.last)
+
+ #Arrange: get to finalization page
+ get finalize_path(order)
+
+ put confirmation_path
+
+ must_respond_with :success
+ controller.session[:order_id].must_be_nil
+ expect(Order.last.status).must_equal 'paid'
+ end
+
+ it 'responds with bad_request if there is no cart' do
+ put confirmation_path
+
+ must_respond_with :bad_request
+ end
+
+ end
+
+ describe 'destroy' do
+
+ let(:order_data) {
+ {
+ order: {
+ name: 'Sherlock Holmes',
+ email: 'smart@ssdetective.com',
+ address: '221B Baker St, London',
+ cc_num: 1234567812345678,
+ cvv: 123,
+ exp_month: 12,
+ exp_year: Date.today.year + 1,
+ zip: 43770
+ }
+ }
+ }
+
+ let(:order_item_data) {
+ {
+ id: Product.first.id,
+ order_item: {
+ quantity: 2,
+ }
+ }
+ }
+
+ it 'succeeds if cart exists and user is the owner of this cart' do
+ #Arrange: THIS user makes order
+ post order_items_path, params: order_item_data
+ must_redirect_to order_path(Order.last)
+
+ #Arrange: edit and update order with buyer info
+ order = Order.last
+ put order_path(order), params: order_data
+ must_redirect_to finalize_path(Order.last)
+
+ #Arrange: get to finalization page
+ get finalize_path(order)
+
+ expect{
+ delete order_path(order)
+ }.must_change('Order.where(status: "cancelled").count', +1)
+
+ must_respond_with :success
+ expect(flash[:result_text]).must_include 'Order Cancelled'
+ expect(Order.last.status).must_equal 'cancelled'
+
+ end
+
+ it 'responds with bad_request if no cart exists' do
+ status = Order.first.status
+
+ expect{
+ delete order_path(Order.first)
+ }.wont_change('Order.where(status: "cancelled").count')
+
+ must_respond_with :bad_request
+ expect(flash[:result_text]).must_include 'Cannot empty nonexistent or unauthorized cart'
+ expect(Order.first.status).must_equal status
+ end
+
+ it 'responds with bad_request if a user tries to delete a cart of a different user' do
+ post order_items_path, params: order_item_data
+ controller.session[:order_id].wont_be_nil
+
+ status = Order.first.status
+
+ expect{
+ delete order_path(Order.first)
+ }.wont_change('Order.where(status: "cancelled").count')
+
+ must_respond_with :bad_request
+ expect(flash[:result_text]).must_include 'Cannot empty nonexistent or unauthorized cart'
+ expect(Order.first.status).must_equal status
+ end
+
+ end
+
+ describe 'nosnacks' do
+ it 'response with bad_request' do
+ get nosnacks_path
+
+ must_respond_with :bad_request
+ end
+ end
+
+ describe 'validations on update action' do
+ let(:order) { Order.new }
+
+ let(:order_data) {
+ {
+ order: {
+ name: 'Sherlock Holmes',
+ email: 'smart@ssdetective.com',
+ address: '221B Baker St, London',
+ cc_num: 1234567812345678,
+ cvv: 123,
+ exp_month: 12,
+ exp_year: Date.today.year + 1,
+ zip: 43770
+ }
+ }
+ }
+
+ let(:existing_order) {Order.first}
+ let(:existing_id) { existing_order.id }
+
+ it 'is valid on create with only id and status recorded' do
+ order.valid?.must_equal true
+ end
+
+ it "is valid on update with properly formatted and completed fields: name, email, address, zip, cvv, exp_month, exp_year, and cc_num; changes the status to 'awaiting confirmation'; and redirects to the finalize page" do
+ order_test = Order.new(order_data[:order])
+
+ order_test.must_be :valid?, "Order data was invalid. Engineer, please fix this test."
+
+ expect {
+ put order_path(existing_id), params: order_data
+ }.wont_change('Order.count')
+
+ must_redirect_to finalize_path(existing_id)
+
+ order = Order.find_by(id: existing_id)
+ expect(order.status).must_equal 'awaiting confirmation'
+
+ expect(order.name).must_equal order_data[:order][:name]
+ expect(order.email).must_equal order_data[:order][:email]
+ expect(order.address).must_equal order_data[:order][:address]
+ expect(order.cc_num).must_equal order_data[:order][:cc_num]
+ expect(order.cvv).must_equal order_data[:order][:cvv]
+ expect(order.exp_month).must_equal order_data[:order][:exp_month]
+ expect(order.exp_year).must_equal order_data[:order][:exp_year]
+ expect(order.zip).must_equal order_data[:order][:zip]
+
+ end
+
+ it 'renders bad request on update if missing credit card information' do
+
+ order.valid?.must_equal true
+ order.save
+
+ order_data[:order][:cc_num] = nil
+
+ expect {
+ patch order_path(order), params: order_data
+ }.wont_change('Order.count')
+
+ must_respond_with :bad_request
+ expect(order.cc_num).must_be_nil
+
+ end
+
+ it 'renders bad request on update if credit card number length is not 16' do
+
+ order.valid?.must_equal true
+ order.save
+
+ order_data[:order][:cc_num] = [*1..15]
+
+ expect {
+ patch order_path(order), params: order_data
+ }.wont_change('Order.count')
+
+ must_respond_with :bad_request
+ expect(order.cc_num).must_be_nil
+
+ order_data[:order][:cc_num] = [*1..17]
+
+ expect {
+ patch order_path(order), params: order_data
+ }.wont_change('Order.count')
+
+ must_respond_with :bad_request
+ expect(order.cc_num).must_be_nil
+
+ end
+
+ it 'renders bad request on update if missing name' do
+
+ order.valid?.must_equal true
+ order.save
+
+ order_data[:order][:name] = nil
+
+ expect {
+ patch order_path(order), params: order_data
+ }.wont_change('Order.count')
+
+ must_respond_with :bad_request
+ expect(order.name).must_be_nil
+
+ end
+
+ it 'renders bad request on update if missing email' do
+
+ order.valid?.must_equal true
+ order.save
+
+ order_data[:order][:email] = nil
+
+ expect {
+ patch order_path(order), params: order_data
+ }.wont_change('Order.count')
+
+ must_respond_with :bad_request
+ expect(order.email).must_be_nil
+
+ end
+
+ it 'renders bad request on update if missing address' do
+
+ order.valid?.must_equal true
+ order.save
+
+ order_data[:order][:address] = nil
+
+ expect {
+ patch order_path(order), params: order_data
+ }.wont_change('Order.count')
+
+ must_respond_with :bad_request
+ expect(order.address).must_be_nil
+
+ end
+
+ it 'renders bad request on update if missing zip' do
+
+ order.valid?.must_equal true
+ order.save
+
+ order_data[:order][:zip] = nil
+
+ expect {
+ patch order_path(order), params: order_data
+ }.wont_change('Order.count')
+
+ must_respond_with :bad_request
+ expect(order.zip).must_be_nil
+
+ end
+
+ it 'renders bad request on update if missing cvv' do
+
+ order.valid?.must_equal true
+ order.save
+
+ order_data[:order][:cvv] = nil
+
+ expect {
+ patch order_path(order), params: order_data
+ }.wont_change('Order.count')
+
+ must_respond_with :bad_request
+ expect(order.cvv).must_be_nil
+
+ end
+
+ it 'renders bad request on update if exp_year is before the current_year' do
+
+ order.valid?.must_equal true
+ order.save
+
+ order_data[:order][:exp_year] = Date.today.year - 1
+
+ expect {
+ patch order_path(order), params: order_data
+ }.wont_change('Order.count')
+
+ must_respond_with :bad_request
+ expect(order.exp_year).must_be_nil
+ expect( flash[:messages] ).must_include :exp_year
+
+ end
+
+ it 'renders bad request on update if exp_year and exp_month correlate to an expired card' do
+
+ order.valid?.must_equal true
+ order.save
+
+ order_data[:order][:exp_year] = Date.today.year
+ order_data[:order][:exp_month] = Date.today.month - 1
+
+ expect {
+ patch order_path(order), params: order_data
+ }.wont_change('Order.count')
+
+ must_respond_with :bad_request
+ expect(order.exp_year).must_be_nil
+ expect(order.exp_month).must_be_nil
+ expect( flash[:messages] ).must_include :exp_month
+
+ end
+
+ it 'raises an error if exp_year is nil' do
+
+ order.valid?.must_equal true
+ order.save
+
+ order_data[:order][:exp_year] = nil
+
+ expect {
+ patch order_path(order), params: order_data
+ }.must_raise ArgumentError
+
+ end
+
+ it 'raises an error if exp_year is nil' do
+
+ order.valid?.must_equal true
+ order.save
+
+ order_data[:order][:exp_month] = nil
+
+ expect {
+ patch order_path(order), params: order_data
+ }.must_raise ArgumentError
+
+ end
+ end
+
+ describe 'summary' do
+
+ let(:search_data) {
+ {
+ search_id: Order.first.id,
+ search_email: Order.first.email
+ }
+ }
+
+ it 'responds with success if a user is accessing the order they just searched as tracked by session[:old_order], renders page regardless (output varies on permissions)' do
+
+ get orders_path, params: search_data
+
+ session[:old_order].wont_be_nil
+
+ get summary_path(session[:old_order])
+
+ must_respond_with 200
+ end
+
+ it 'responds with success if a logged_in_merchant is accessing an order that contains at least one of their products, renders page regardless (output varies on permissions)' do
+
+ iron_chef = merchants(:iron_chef)
+ order_three = orders(:order_three)
+
+ perform_login(iron_chef)
+
+ get summary_path(order_three), params: {id: 3}
+
+ must_respond_with 200
+ end
+
+ end
+
+end
diff --git a/test/controllers/pages_controller_test.rb b/test/controllers/pages_controller_test.rb
new file mode 100644
index 0000000000..c9a686b8f4
--- /dev/null
+++ b/test/controllers/pages_controller_test.rb
@@ -0,0 +1,7 @@
+require "test_helper"
+
+describe PagesController do
+ # it "must be a real test" do
+ # flunk "Need real tests"
+ # end
+end
diff --git a/test/controllers/products_controller_test.rb b/test/controllers/products_controller_test.rb
new file mode 100644
index 0000000000..aa3b87c8f7
--- /dev/null
+++ b/test/controllers/products_controller_test.rb
@@ -0,0 +1,191 @@
+require "test_helper"
+require "pry"
+
+describe ProductsController do
+
+ describe "Logged in merchants" do
+
+ before do
+ perform_login(merchants(:bake_off))
+ end
+
+ describe "access for logged in merchants" do
+ it "logged in merchants can access the homepage" do
+ get home_path
+ must_respond_with :success
+ end
+
+ it "logged in merchants can access the all products index page" do
+ products = Product.all
+ get products_path
+ must_respond_with :success
+ end
+
+ it "logged in merchants can access the searched products index page" do
+ product = Product.first
+ search_data = { search: product.name.to_s}
+
+ get products_path, params: search_data
+
+ must_respond_with 200
+ end
+
+ it "logged in merchants can access the show page for a product" do
+ product_id = Product.first.id
+ get product_path(product_id)
+ must_respond_with :success
+ end
+
+ it "logged in merchants cannot access the show page for a product that doesn't exist" do
+ product_id = Product.first.id + 1
+
+ get product_path(product_id)
+ must_respond_with :not_found
+ end
+
+ it "logged in merchants can access the new product form" do
+ get new_product_path
+ must_respond_with :success
+ end
+ end
+
+ describe "update-ability for logged-in merchants" do
+ describe "create" do
+ it "creates a product with valid data" do
+
+ product_data = {
+ product: {
+ name: "Fuzzy bunnies",
+ price: 100,
+ inventory: 2,
+ merchant_id: 1
+ }
+ }
+
+ expect {
+ post products_path, params: product_data
+ }.must_change('Product.count', +1)
+
+ must_redirect_to product_path(Product.last)
+ end
+
+ it "will not create a product, given incomplete data" do
+
+ product_data = {
+ product: {
+ price: 100,
+ inventory: 2,
+ merchant_id: 1
+ }
+ }
+
+ expect {
+ post products_path, params: product_data
+ }.wont_change ('Product.count')
+
+ must_respond_with :bad_request
+ end
+ end
+
+ describe "edit" do
+ it "succeeds for a product ID that exists" do
+ get edit_product_path(Product.first)
+ must_respond_with :success
+ end
+
+
+ it "redirects for a product that doesn't exist" do
+ product = Product.first
+ order_items = OrderItem.all
+ order_items.destroy_all
+ product.destroy
+
+ get edit_product_path (product)
+ flash[:status].must_equal :failure
+ must_respond_with 302
+
+
+ end
+ end
+
+ describe "update" do
+ let (:product_hash) {
+ {
+ product: {
+ name: 'Fuzzy Bunnies',
+ price: 10,
+ inventory: 20,
+ }
+ }
+ }
+ it "succeeds for valid data and an extant work ID" do
+ product = products(:butter).id
+ expect {
+ patch product_path(product), params: product_hash
+ }.wont_change 'Product.count'
+
+ must_respond_with :redirect
+
+ product = Product.find_by(id: product)
+ expect(product.name).must_equal 'Fuzzy Bunnies'
+ expect(product.price).must_equal 10
+ expect(product.inventory).must_equal 20
+ end
+
+ it "renders bad_request for badf data" do
+ product = products(:butter).id
+ product_hash[:product][:price] = 0
+ expect {
+ patch product_path(product), params: product_hash
+ }.wont_change 'Product.count'
+
+ must_respond_with :bad_request
+
+ end
+ end
+ end
+ end
+
+ describe "Guest users" do
+ describe "access for guest users" do
+ it "guest users can access the homepage" do
+ get home_path
+ must_respond_with :success
+ end
+
+ it "guest users can access the product index page" do
+ products = Product.all
+ get products_path
+ must_respond_with :success
+ end
+
+ it "guest users can access the show page for a product" do
+ product_id = Product.first.id
+ get product_path(product_id)
+ must_respond_with :success
+ end
+
+ # I think this failure has something to do with single-use
+ it "guest users cannot access the show page for a product that doesn't exist" do
+
+ get product_path(Product.last.id + 1)
+
+ must_respond_with :not_found
+
+ end
+
+ it "guest users cannot access the new product form" do
+ get new_product_path
+ must_redirect_to root_path
+ flash[:status].must_equal :failure
+ end
+
+ it "guest users cannot access the edit product form" do
+ product = Product.first
+ get edit_product_path(product)
+ must_redirect_to root_path
+ flash[:status].must_equal :failure
+ end
+ end
+ end
+end
diff --git a/test/controllers/reviews_controller_test.rb b/test/controllers/reviews_controller_test.rb
new file mode 100644
index 0000000000..3752ff2a04
--- /dev/null
+++ b/test/controllers/reviews_controller_test.rb
@@ -0,0 +1,100 @@
+require "test_helper"
+require 'pry'
+
+describe ReviewsController do
+
+ describe "Logged in merchants" do
+
+ before do
+ bake_off = merchants(:bake_off)
+ perform_login(merchants(:bake_off))
+ @thyme = products(:thyme)
+
+ end
+
+ describe "create-ability for logged-in merchants" do
+
+ let(:review_data) {
+ {
+ id: @thyme.id ,
+ review: {
+ rating: 5,
+ description: "awesome",
+ }
+ }
+ }
+
+ it "can create a review for products that aren't their own" do
+
+
+ expect{
+ post create_review_path(@thyme.id), params: review_data
+ }
+
+ must_redirect_to root_path
+ flash[:status].must_equal :success
+
+ end
+
+ it "cannot create a review for their own products" do
+ steak = products(:steak)
+
+ review_data[:id] = steak.id
+
+ expect{
+ post create_review_path(steak.id), params: review_data
+ }
+
+ must_redirect_to root_path
+ flash[:status].must_equal :failure
+ end
+
+ it "cannot create a review for a product that doesn't exist" do
+ review_data
+ steak = products(:steak)
+ steak.id = nil
+
+ review_data[:id] = nil
+
+ expect{
+ post create_review_path(steak.id), params: review_data
+ }
+
+ # binding.pry
+
+ must_redirect_to root_path
+ flash[:status].must_equal :failure
+
+ end
+ end
+ end
+
+ describe "Guest users" do
+ describe "Guest users can create a review" do
+
+ it "guests can create a review for a product that exists" do
+ review_data = {
+ review: {
+ rating: 5,
+ description: "great",
+ product_id: 3
+ }
+ }
+
+ review = Review.new(review_data(:review))
+
+ expect {
+ post create_review_path(review)
+ }.must_change('Review.count', +1)
+
+
+ must_respond_with :success
+
+ end
+
+ it "guests cannot create a review for a product that doesn't exist" do
+
+ end
+ end
+ end
+end
diff --git a/test/fixtures/.keep b/test/fixtures/.keep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/test/fixtures/categories.yml b/test/fixtures/categories.yml
new file mode 100644
index 0000000000..9ca04b9f1d
--- /dev/null
+++ b/test/fixtures/categories.yml
@@ -0,0 +1,17 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+atlantis:
+ name: Atlantean
+ # products: thyme, donut, sherbert, pickle
+
+pandora:
+ name: Pandoran
+ # products: thyme, donut, sherbert, bread
+
+shangri:
+ name: Shangri-La
+ # products: sherbert, pickle
+
+asgard:
+ name: Asgardian
+ # products: butter, steak, sherbert, bread, taco
diff --git a/test/fixtures/files/.keep b/test/fixtures/files/.keep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/test/fixtures/merchants.yml b/test/fixtures/merchants.yml
new file mode 100644
index 0000000000..079ee341e4
--- /dev/null
+++ b/test/fixtures/merchants.yml
@@ -0,0 +1,25 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+iron_chef:
+ name: Iron Chef
+ email: iron@chef.com
+ uid: 1
+ provider: github
+
+hells:
+ name: Hells Kitchen
+ email: hells@kitchen.com
+ uid: 2
+ provider: github
+
+bake_off:
+ name: Bake Off
+ email: bake@off.com
+ uid: 3
+ provider: github
+
+ugly:
+ name: Ugly Delicious
+ email: ugly@delicious.com
+ uid: 4
+ provider: github
diff --git a/test/fixtures/order_items.yml b/test/fixtures/order_items.yml
new file mode 100644
index 0000000000..e2a3e31472
--- /dev/null
+++ b/test/fixtures/order_items.yml
@@ -0,0 +1,37 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+order_item_1:
+ quantity: 1
+ product: butter
+ order: order_one
+ status: false
+
+order_item_2:
+ quantity: 2
+ product: bread
+ order: order_one
+ status: false
+
+order_item_3:
+ quantity: 1
+ product: steak
+ order: order_one
+ status: false
+
+order_item_4:
+ quantity: 10
+ product: sherbert
+ order: order_two
+ status: false
+
+order_item_5:
+ quantity: 5
+ product: taco
+ order: order_three
+ status: true
+
+order_item_6:
+ quantity: 1
+ product: thyme
+ order: order_three
+ status: true
diff --git a/test/fixtures/orders.yml b/test/fixtures/orders.yml
new file mode 100644
index 0000000000..4dba50a360
--- /dev/null
+++ b/test/fixtures/orders.yml
@@ -0,0 +1,38 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+order_one:
+ email: email@email.com
+ address: 123 4th street, City, WA 12345
+ cc_num: 1234567812345678
+ order_placed: 2018-10-18 12:57:30
+ status: paid
+ zip: 12345
+ name: Mrs. Buttersworth
+ cvv: 123
+ exp_month: 10
+ exp_year: 2080
+
+order_two:
+ email: email@email.com
+ address: 123 4th street, City, WA 12345
+ cc_num: 1234567812345678
+ order_placed: 2018-10-17 12:57:30
+ status: paid
+ zip: 12345
+ name: Pepper Potts
+ cvv: 123
+ exp_month: 10
+ exp_year: 2080
+
+
+order_three:
+ email: email@email.com
+ address: 123 4th street, City, WA 12345
+ cc_num: 1234567812345678
+ order_placed: 2018-10-16 12:57:30
+ status: paid
+ zip: 12345
+ name: Mighty Oprah
+ cvv: 123
+ exp_month: 10
+ exp_year: 2080
diff --git a/test/fixtures/products.yml b/test/fixtures/products.yml
new file mode 100644
index 0000000000..cbae17799f
--- /dev/null
+++ b/test/fixtures/products.yml
@@ -0,0 +1,87 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+thyme:
+ name: Thyme
+ price: 500
+ description: Thyme is money. Money is Thyme.
+ img_file: https://loremflickr.com/320/240/fruit
+ merchant: iron_chef
+ categories: atlantis, pandora
+ inventory: 10
+ status: true
+
+butter:
+ name: Butter
+ price: 500
+ description: You butter believe it.
+ img_file: https://loremflickr.com/320/240/fruit
+ merchant: hells
+ categories: asgard
+ inventory: 10
+ status: true
+
+
+steak:
+ name: Steak
+ price: 1000
+ description: Nice to meat you.
+ img_file: https://loremflickr.com/320/240/fruit
+ merchant: bake_off
+ categories: asgard
+ inventory: 10
+ status: true
+
+
+donut:
+ name: Donut
+ price: 500
+ description: Donut take us lightly!
+ img_file: https://loremflickr.com/320/240/fruit
+ merchant: ugly
+ categories: pandora, atlantis
+ inventory: 10
+ status: true
+
+
+sherbert:
+ name: Sherbet
+ price: 700
+ description: Go shorty, it's sherbert day!
+ img_file: https://loremflickr.com/320/240/fruit
+ merchant: ugly
+ categories: pandora, shangri, atlantis, asgard
+ inventory: 10
+ status: true
+
+
+bread:
+ name: bread
+ price: 500
+ description: The path of yeast resistance.
+ img_file: https://loremflickr.com/320/240/fruit
+ merchant: ugly
+ categories: asgard, pandora
+ inventory: 10
+ status: true
+
+
+pickle:
+ name: Pickles
+ price: 850
+ description: I'm kind of a big dill.
+ img_file: https://loremflickr.com/320/240/fruit
+ merchant: hells
+ categories: atlantis, shangri
+ inventory: 10
+ status: true
+
+
+taco:
+ name: Taco
+ price: 500
+ description: Wanna Taco bout it? It's nacho business!
+ img_file: https://loremflickr.com/320/240/fruit
+ merchant: iron_chef
+ categories: asgard
+ inventory: 10
+ status: true
diff --git a/test/fixtures/reviews.yml b/test/fixtures/reviews.yml
new file mode 100644
index 0000000000..d4fcd5abf1
--- /dev/null
+++ b/test/fixtures/reviews.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+review_one:
+ rating: 4
+ description: get in my belly
+ product: bread
+
+review_two:
+ rating: 1
+ description: dont get in my belly
+ product: steak
diff --git a/test/helpers/.keep b/test/helpers/.keep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/test/integration/.keep b/test/integration/.keep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/test/mailers/.keep b/test/mailers/.keep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/test/models/.keep b/test/models/.keep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/test/models/category_test.rb b/test/models/category_test.rb
new file mode 100644
index 0000000000..61ecf38130
--- /dev/null
+++ b/test/models/category_test.rb
@@ -0,0 +1,44 @@
+require "test_helper"
+
+describe Category do
+ describe "relations" do
+
+ it "has a list of products" do
+ category = categories(:atlantis)
+ category.must_respond_to :products
+ category.products.each do |product|
+ product.must_be_kind_of Product
+ end
+ end
+
+ it "is included in a list of products" do
+ category = categories(:atlantis)
+ product = products(:thyme)
+ category.must_respond_to :products
+ product.categories.each do |category|
+ category.must_be_kind_of Category
+ end
+ end
+ end
+
+ describe "validations" do
+ it "requires a name" do
+ category = Category.new
+ category.valid?.must_equal false
+ category.errors.messages.must_include :name
+ end
+
+ it "requires a unique name" do
+ category1 = Category.new(name: "fake")
+ category1.save!
+ category2 = Category.new(name: "fake")
+
+ category2.valid?.must_equal false
+ category2.errors.messages.must_include :name
+ end
+ end
+end
+
+# has_and_belongs_to_many :products
+#
+# validates :name, uniqueness: true
diff --git a/test/models/merchant_test.rb b/test/models/merchant_test.rb
new file mode 100644
index 0000000000..edc283cfa0
--- /dev/null
+++ b/test/models/merchant_test.rb
@@ -0,0 +1,119 @@
+require "test_helper"
+
+describe Merchant do
+
+ before do
+ @ironchef = merchants(:iron_chef)
+ end
+
+ it "must be valid" do
+ Merchant.all.each do |m|
+ expect(m.valid?).must_equal true
+ end
+ end
+
+
+ describe "merchant model reltionship" do
+
+ it "merchant has a reltionship to order items through products" do
+
+ expect(@ironchef.order_items.last.product.name).must_equal "Taco"
+ expect(@ironchef.order_items.first.product.name).must_equal "Thyme"
+ expect(@ironchef.id).must_equal @ironchef.products.first.merchant_id
+ expect(@ironchef.id).must_equal @ironchef.products.last.merchant_id
+ end
+
+
+ it "merchant has many products" do
+
+ thyme = products(:thyme)
+ taco = products(:taco)
+
+ @ironchef.must_respond_to :products
+
+ @ironchef.products.each do |product|
+ product.must_be_kind_of Product
+ end
+ end
+
+
+
+ it "merchant has relationship with order items" do
+ order_item = order_items(:order_item_6)
+
+ thyme = products(:thyme)
+ taco = products(:taco)
+
+ @ironchef.must_respond_to :order_items
+ end
+ end
+
+
+ describe "merchant model validation checks" do
+
+ it "requires a name and an email" do
+ merchant = Merchant.new(uid: 19, provider: 'github')
+ merchant.valid?.must_equal false
+ merchant.errors.messages.must_include :name
+ merchant.errors.messages.must_include :email
+ end
+
+ it "requires a unique name" do
+ merchant1 = Merchant.new(name: @ironchef.name, email: "someone@gmail.com" )
+
+ merchant1.valid?.must_equal false
+
+ merchant1.errors.messages.must_include :name
+ end
+
+
+ it "requires unique email" do
+ merchant2 = Merchant.new(name: "someone", email: @ironchef.email )
+
+ merchant2.valid?.must_equal false
+
+ merchant2.errors.messages.must_include :email
+ end
+
+
+ it "Passes when name is present and is unique" do
+ valid_merchant = Merchant.new(name: "DanDeeLions", email: "dandeelions@gmail.com" )
+
+ valid_merchant.valid?.must_equal true
+ end
+
+ it "Passes when email is present and unique" do
+ valid_merchant = Merchant.new(name: "Email", email: "email@gmail.com" )
+
+ valid_merchant.valid?.must_equal true
+ end
+ end
+
+ describe "calculates accurate data" do
+
+ before do
+ order1 = order_items(:order_item_6)
+ order2 = order_items(:order_item_5)
+ end
+
+ it "merchant order total method return accurate price for two orders " do
+
+ @ironchef.order_items.length.must_equal 2
+ expect(@ironchef.order_total).must_equal 3000
+ end
+
+ it "returns right tax based on a order item " do
+
+ expect(@ironchef.order_total).must_equal 3000
+ expect(@ironchef.tax).must_equal 300
+
+ end
+
+
+ it "returns right total with taxes" do
+ expect(@ironchef.order_total).must_equal 3000
+ expect(@ironchef.tax).must_equal 300
+ expect(@ironchef.total_tax).must_equal 3300
+ end
+ end
+end
diff --git a/test/models/order_item_test.rb b/test/models/order_item_test.rb
new file mode 100644
index 0000000000..7401ed1306
--- /dev/null
+++ b/test/models/order_item_test.rb
@@ -0,0 +1,167 @@
+require "test_helper"
+
+describe OrderItem do
+
+ # VALIDATIONS TESTS
+ describe 'validations' do
+ let(:order_item) { order_items(:order_item_1) }
+
+ it 'is not valid without a product' do
+ order_item.product_id = nil
+
+ expect(order_item).must_be :invalid?
+ expect
+ end
+
+ it 'is not valid without an order' do
+
+ order_item.order = nil
+
+ expect(order_item).must_be :invalid?
+ end
+
+ it 'is not valid without a quantity' do
+
+ order_item.quantity = nil
+
+ expect(order_item).must_be :invalid?
+
+ end
+
+ it 'is not valid with a quantity less than 0' do
+ order_item.quantity = -10
+
+ expect(order_item).must_be :invalid?
+ end
+
+ it 'is not valid with a quantity equal to 0' do
+ order_item.quantity = 0
+
+ expect(order_item).must_be :invalid?
+
+ end
+
+ it 'is not valid with a quantity input other than an integer' do
+ order_item.quantity = "you should fail"
+
+ expect(order_item).must_be :invalid?
+ end
+
+ it 'is valid when all fields are present' do
+ expect(order_item).must_be :valid?
+ end
+
+
+ it 'is not valid when quantity exceeds product inventory' do
+ stock = order_item.product.inventory
+
+ order_item.quantity = stock + 1
+
+ expect(order_item).must_be :invalid?
+ end
+
+
+ # RELATIONS TESTS
+ describe 'relations' do
+ let(:order_item) { order_items(:order_item_6) }
+
+ # products
+ it 'must relate to a product' do
+
+ expect(order_item).must_respond_to :product
+ expect(order_item.product).must_equal products(:thyme)
+ end
+
+ it 'can set the product via product instance' do
+
+ new_item = OrderItem.new(quantity: 1, order: orders(:order_two))
+
+ new_item.product = products(:steak)
+
+ expect(new_item.product_id).must_equal products(:steak).id
+ end
+
+ it 'can set the product via product id' do
+
+ new_item = OrderItem.new(quantity: 1, order: orders(:order_two))
+
+ new_item.product_id = products(:steak).id
+
+ expect(new_item.product).must_equal products(:steak)
+ end
+
+ # orders
+ it 'must relate to an order' do
+
+ expect(order_item).must_respond_to :order
+ expect(order_item.order).must_equal orders(:order_three)
+ end
+
+ it 'can set the order via order instance' do
+ new_item = OrderItem.new(quantity: 1, product: products(:taco))
+
+ new_item.order_id = orders(:order_two).id
+
+ expect(new_item.order).must_equal orders(:order_two)
+ end
+
+ it 'can set the order via order id' do
+ new_item = OrderItem.new(quantity: 1, product: products(:taco))
+
+ new_item.order = orders(:order_two)
+
+ expect(new_item.order_id).must_equal orders(:order_two).id
+ end
+ end
+ end
+
+
+ # MODEL TESTS
+ describe 'custom model methods' do
+
+ let(:order_item) { order_items(:order_item_1) }
+
+ describe 'available_stock' do
+ it 'must return an array of Integers from 1 to the associated product inventory' do
+ stock = order_item.product.inventory
+ result = order_item.available_stock
+
+ expect(result).must_respond_to :each
+ expect(result.first).must_equal 1
+ expect(result.last).must_equal stock
+ end
+
+ it 'must return nil if associated product inventory is less than 1' do
+ order_item.product.inventory = 0
+ expect(order_item.available_stock).must_equal [0]
+ end
+ end
+
+ describe 'item_total' do
+ it 'must return the line item total for an order_item' do
+ product = order_item.product.price
+ quantity = order_item.quantity
+
+ expected_result = product * quantity
+
+ expect(order_item.item_total).must_equal expected_result
+ end
+ end
+
+ describe 'product_name' do
+ it 'must return the name of the product' do
+ name = order_item.product.name
+
+ expect(order_item.product_name).must_equal name
+ end
+ end
+
+ describe 'order_status' do
+ it 'must return the status of the product' do
+ status = order_item.order.status
+
+ expect(order_item.order_status).must_equal status
+ end
+ end
+ end
+end
diff --git a/test/models/order_test.rb b/test/models/order_test.rb
new file mode 100644
index 0000000000..5296c6afaf
--- /dev/null
+++ b/test/models/order_test.rb
@@ -0,0 +1,230 @@
+require "test_helper"
+
+describe Order do
+
+ describe 'relations' do
+ let(:order) { Order.first }
+
+ it 'has some order_items' do
+ order.must_respond_to :order_items
+ order.order_items.each do |order_item|
+ order_item.must_be_kind_of OrderItem
+ end
+ end
+
+ it 'has some products through order_items' do
+ order.must_respond_to :products
+ order.products.each do |order_item|
+ order_item.must_be_kind_of Product
+ end
+ end
+ end
+
+ describe 'validations' do
+ let(:order) {
+ Order.new
+ }
+
+ let(:order_data) {
+ {
+ order: {
+ name: 'Sherlock Holmes',
+ email: 'smart@ssdetective.com',
+ address: '221B Baker St, London',
+ cc_num: 1234567812345678,
+ cvv: 123,
+ exp_month: 12,
+ exp_year: Date.today.year + 1,
+ zip: 43770,
+ status: 'awaiting confirmation'
+ }
+ }
+ }
+
+ let(:existing_order) {Order.first}
+ let(:existing_id) { existing_order.id }
+
+ it 'is valid on create with only id and status recorded' do
+ order.valid?.must_equal true
+ end
+
+ it 'is invalid without a status' do
+ order = Order.create(order_data[:order])
+ x = Order.update(order.id, status: nil)
+ x.status.must_be_nil
+ x.valid?.must_equal false
+ end
+
+
+ end
+
+ describe 'subtotal method' do
+ it 'returns the sum of all of the order items of this order' do
+ order = Order.first
+
+ sum = 0
+ order.order_items.each do |o_item|
+ sum += o_item.product.price * o_item.quantity
+ end
+
+ order.subtotal.must_be_kind_of BigDecimal
+ order.subtotal.must_equal sum
+ end
+ end
+
+ describe 'taxes method' do
+ it 'returns the taxes for the order subtotal' do
+ order = Order.first
+
+ sum = 0
+ order.order_items.each do |o_item|
+ sum += o_item.product.price * o_item.quantity
+ end
+
+ expected_result = sum * 0.101
+
+ order.taxes.must_be_close_to expected_result
+
+ end
+ end
+
+ describe 'total method' do
+ it 'returns the sum + tax for this order' do
+ order = Order.first
+
+ sum = 0
+ order.order_items.each do |o_item|
+ sum += o_item.product.price * o_item.quantity
+ end
+ sum *= (1 + 0.101)
+
+ order.total.must_be_kind_of BigDecimal
+ order.total.must_equal sum
+ end
+ end
+
+ describe 'Class method: months' do
+ it 'returns an array from 1 through 12' do
+ expected_array = [*1..12]
+
+ Order.months.must_equal expected_array
+
+ end
+ end
+
+ describe 'Class method: years' do
+ it 'returns an array of 30 years starting from the current year' do
+ result = Order.years
+
+ result.first.must_equal Date.today.year
+ result.last.must_equal Date.today.year + 29
+ result.length.must_equal 30
+
+ end
+ end
+
+ describe 'Class method: get_current_month' do
+ it 'returns the current month' do
+ result = Order.get_current_month
+
+ result.must_equal Date.today.month
+ end
+ end
+
+ describe 'Class method: get_current_year' do
+ it 'returns the current year' do
+ result = Order.get_current_year
+
+ result.must_equal Date.today.year
+ end
+ end
+
+ describe 'validate method: cant_be_expired' do
+
+ let(:order_data) {
+ {
+ order: {
+ name: 'Sherlock Holmes',
+ email: 'smart@ssdetective.com',
+ address: '221B Baker St, London',
+ cc_num: 1234567812345678,
+ cvv: 123,
+ exp_month: Date.today.month,
+ exp_year: Date.today.year+1,
+ zip: 43770,
+ status: 'paid'
+ }
+ }
+ }
+
+ let(:good_month) { Date.today.month + 1 }
+ let(:bad_year) { Date.today.year - 1 }
+ let(:bad_month) { Date.today.month - 1 }
+ let(:order) {Order.create}
+
+ it 'succeeds if given an expiration month and year than indicates a non-expired card' do
+
+ order = Order.create(order_data[:order])
+ x = Order.update(order.id, exp_month: good_month)
+
+ expect(x.errors.messages).must_be_empty
+ end
+
+ it 'raises an ArgumentError only on update if exp_year or exp_month are nil' do
+
+ order.must_be :valid?, "Order params have some invalid parameters. Please fix."
+
+ expect{Order.update(order.id, exp_month: nil)}.must_raise ArgumentError
+ expect{Order.update(order.id, exp_year: nil)}.must_raise ArgumentError
+
+ end
+
+ it 'adds messages to errors only on update if exp_year indicates an expired credit card' do
+ order_data[:order][:exp_month] = good_month
+ order = Order.create(order_data[:order])
+
+ x = Order.update(order.id, exp_year: bad_year)
+ expect(x.errors.messages).must_include :exp_year
+ end
+
+ it 'adds messages to errors only on update if exp_year and exp_month indicates an expired credit card' do
+ order_data[:order][:exp_year] = Date.today.year
+ order = Order.create(order_data[:order])
+
+ x = Order.update(order.id, exp_month: bad_month)
+ expect(x.errors.messages).must_include :exp_month
+ end
+ end
+
+ describe 'Class method search' do
+
+ let(:order) { Order.first }
+ let(:id) {order.id}
+ let(:email) {order.email}
+
+ it 'returns an array of length 1 with the instance of Order that matches both search parameters' do
+ found_record = Order.search(id, email)
+ expect(found_record.length).must_equal 1
+ expect(found_record[0]).must_equal order
+ end
+
+ it 'returns an empty array when there is no match in the database' do
+ found_record = Order.search(0, 'fake@email.com')
+ expect(found_record).must_be_empty
+ end
+ end
+
+ describe 'get_cc' do
+ it 'returns a string of a 16 digit number formatted like XXXX-XXXX-XXXX-1111' do
+ order = Order.first
+ cc = order.cc_num.to_s
+
+ expect(order.get_cc).must_equal "XXXX-XXXX-XXXX-#{cc[-4..-1]}"
+
+
+
+ end
+ end
+
+
+end
diff --git a/test/models/product_test.rb b/test/models/product_test.rb
new file mode 100644
index 0000000000..81f37f5963
--- /dev/null
+++ b/test/models/product_test.rb
@@ -0,0 +1,82 @@
+require "test_helper"
+require "pry"
+
+describe Product do
+ describe "relations" do
+ it "has a list of categories" do
+ product = products(:thyme)
+ product.must_respond_to :categories
+ product.categories.each do |category|
+ category.must_be_kind_of Category
+ end
+ end
+
+ it "is included in a list of categories" do
+ category = categories(:atlantis)
+ product = products(:thyme)
+ category.must_respond_to :products
+ category.products.each do |product|
+ product.must_be_kind_of Product
+ end
+ end
+
+ it "has a list of order_items" do
+ product = products(:thyme)
+ product.must_respond_to :order_items
+ product.order_items.each do |item|
+ item.must_be_kind_of OrderItem
+ end
+ end
+
+ it "is associated with a merchant" do
+ product = products(:thyme)
+ product.merchant.must_be_kind_of Merchant
+ end
+ end
+
+ describe "validations" do
+ it "requires a name" do
+ product = Product.new(price: 1)
+ product.valid?.must_equal false
+ product.errors.messages.must_include :name
+ end
+
+ it "requires a price" do
+ product = Product.new(name: "fake")
+ product.valid?.must_equal false
+ product.errors.messages.must_include :price
+ end
+
+ it "requires a price greater than 0" do
+ product = Product.new(name: "fake", price: 0)
+ product.valid?.must_equal false
+ product.errors.messages.must_include :price
+ end
+
+
+ it "requires a unique name" do
+ product1 = Product.new(name: "fake", merchant: merchants(:iron_chef), price: 1)
+ product1.save!
+ product2 = Product.new(name: "fake", merchant: merchants(:iron_chef), price: 1)
+
+ product2.valid?.must_equal false
+ product2.errors.messages.must_include :name
+ end
+
+ it "does not require a unique price" do
+ product1 = Product.new(name: "fake", price: 1, merchant: merchants(:iron_chef))
+ product1.save!
+ product2 = Product.new(name: "faker", price: 1, merchant: merchants(:iron_chef))
+
+ product2.valid?.must_equal true
+ end
+ end
+
+end
+
+# has_and_belongs_to_many :categories
+# belongs_to :merchant
+# has_many :order_items
+#
+# validates :name, presence: true, uniqueness: true
+# validates :price, presence: true, numericality: {greater_than: 0}
diff --git a/test/models/review_test.rb b/test/models/review_test.rb
new file mode 100644
index 0000000000..d3ddd96651
--- /dev/null
+++ b/test/models/review_test.rb
@@ -0,0 +1,57 @@
+require "test_helper"
+
+describe Review do
+ # VALIDATIONS
+ describe 'validations' do
+ let(:review_one) { reviews(:review_one)}
+
+ it 'is not valid without a product' do
+
+ review_one.product = nil
+
+ expect(review_one).must_be :invalid?
+ end
+
+ it 'is valid with all fields present' do
+ expect(review_one).must_be :valid?
+ end
+
+ end
+
+ # RELATIONS
+ describe 'relations' do
+ let(:review_two) { reviews(:review_two)}
+
+ it 'must relate to a product' do
+ expect(review_two).must_respond_to :product
+ expect(review_two.product).must_equal products(:steak)
+ end
+
+ it 'can set the review via a review instance' do
+ thyme = products(:thyme)
+
+ review = Review.new(rating: 5, description: "best thing ever", product_id: thyme.id)
+
+ expect(review).must_be_kind_of Review
+ expect(review.product).must_be_kind_of Product
+
+ end
+
+ it 'can set the review for the correct product' do
+
+ thyme = products(:thyme)
+
+ review = Review.new(rating: 5, description: "best thing ever", product_id: thyme.id)
+
+ review.save
+
+ expect(thyme.reviews.first.product_id).must_equal thyme.id
+ expect(thyme.reviews.first.description).must_equal "best thing ever"
+
+ expect(thyme.reviews.first.rating).must_equal 5
+
+ end
+
+
+ end
+end
diff --git a/test/system/.keep b/test/system/.keep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/test/test_helper.rb b/test/test_helper.rb
new file mode 100644
index 0000000000..a425a2e77b
--- /dev/null
+++ b/test/test_helper.rb
@@ -0,0 +1,57 @@
+require 'simplecov'
+SimpleCov.start 'rails' do
+ add_filter '/lib/'
+ add_filter '/vendor/'
+ add_filter '/app/channels'
+ add_filter '/app/jobs'
+ add_filter '/app/mailers'
+end
+
+ENV["RAILS_ENV"] = "test"
+require File.expand_path("../../config/environment", __FILE__)
+require "rails/test_help"
+require "minitest/rails"
+require "minitest/reporters" # for Colorized output
+# For colorful output!
+Minitest::Reporters.use!(
+ Minitest::Reporters::SpecReporter.new,
+ ENV,
+ Minitest.backtrace_filter
+)
+
+
+# To add Capybara feature tests add `gem "minitest-rails-capybara"`
+# to the test group in the Gemfile and uncomment the following:
+# require "minitest/rails/capybara"
+
+# Uncomment for awesome colorful output
+# require "minitest/pride"
+
+class ActiveSupport::TestCase
+ # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
+ fixtures :all
+ # Add more helper methods to be used by all tests here...
+
+
+
+ def setup
+ OmniAuth.config.test_mode = true
+ end
+
+ def mock_merch_hash(merchant)
+ return {
+ provider: merchant.provider,
+ uid: merchant.uid,
+ info: {
+ name: merchant.name,
+ email: merchant.email
+ }
+ }
+ end
+
+ def perform_login(merchant)
+ OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new(mock_merch_hash(merchant))
+ get merchant_callback_path(:github)
+ end
+
+end
diff --git a/tmp/.keep b/tmp/.keep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/vendor/.keep b/vendor/.keep
new file mode 100644
index 0000000000..e69de29bb2