+
+
+
+<% end %>
+
diff --git a/app/views/orders/index.html.erb b/app/views/orders/index.html.erb
new file mode 100644
index 0000000000..06d3f4c585
--- /dev/null
+++ b/app/views/orders/index.html.erb
@@ -0,0 +1,53 @@
+
Shopping Cart
+
+
+
+
+
+
+
+
Quantity
+
Item
+
Cost
+
Status
+
+
+
+ <% total = 0 %>
+ <% @shopping_cart.order_items.each do |order_item| %>
+ <% product = Product.find_by(id: order_item.product_id) %>
+ <% total += (product.price * order_item.quantity) %>
+
+
<%= link_to "x", order_item_path(order_item.id), method: :delete, class: "remove-btn", data: {confirm: 'Are you sure you want remove this item from your cart?' } %>
+
+ BrainEaze is your one stop shop for educational games, meditational resources, appeals and neurotropics and much more.
+ Rejuvenate the mind and activate the nuerons!
+
+
diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb
new file mode 100644
index 0000000000..88ae28fd9e
--- /dev/null
+++ b/app/views/products/show.html.erb
@@ -0,0 +1,71 @@
+
+
+
+
+
<%= @product.name %>
+
+
+
Product Reviews
+ <%= link_to "Add new review", new_product_review_path(@product.id) %>
+
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..0c34020ecf
--- /dev/null
+++ b/config/credentials.yml.enc
@@ -0,0 +1 @@
+GMbIIAQ/5NRffX7iCiCBcZuW2u8rzZi5DaeBXluszYpMVKcav3AkJwB8CejAmhS94WtBtZqL9eBmTXwYr55znE7fd2qwCwJ7teizjst/V4aZAimUW1osSkxo28I6QjtHz67QZxmHhafDHZu5H7p9R04sIWeaw1sZPz59OCcYQb4XintKfAYKTkMV01P5LScnaTFwWi76KuT0xFhLd094CAFKIPcPIc4WMuMfkVsBVBa1SFSPhrHBt7dNVWH628rw1N5AlmdiybSn5lumEcJjzSQkG34xEL+83s3TZDCwMHCqZN6d0cwueJ8NiKGs3GTaP3VBLxKIz4SaEuQxOKk7JEs7ljagt/tJRx7Oz/+yxVSqg94hqYdRQKW8FBjhMnDZQvV3vRvC700I3A4yuhb1XrV2OX7HuDFdh/Lw--x2kYxhic6KQpmp1d--W1YDfTdRLPCag+7y0G50UQ==
\ 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..411f5ffc4f
--- /dev/null
+++ b/config/routes.rb
@@ -0,0 +1,40 @@
+Rails.application.routes.draw do
+ # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
+
+ # root 'products#index', as: 'root'
+
+ root 'products#root', as:'root'
+
+ get "/auth/:provider/callback", to: "sessions#create", as: 'auth_callback'
+
+ resources :order_items, only: [:create, :update, :destroy]
+
+ patch '/order_item/:id/update_status', to: 'order_items#update_order_item_status', as: 'update_order_item_status'
+
+ resources :orders, only: [:show, :create, :update, :destroy]
+
+ get '/checkout', to: 'orders#edit', as: 'checkout'
+
+ get '/cart', to: 'orders#index', as: 'cart'
+
+ resources :products, except: [:new, :create, :edit, :update, :destroy] do
+ resources :reviews, only: [:new, :create, :index]
+ end
+
+ resources :categories, except: [:destroy] do
+ resources :products, only: [:index]
+ end
+
+ resources :merchants, only: [:index] do
+ resources :products, except: [:show, :destroy] do
+ member do
+ post :change_status
+ end
+ end
+ end
+
+ get '/dashboard', to: 'merchants#show', as: 'dashboard'
+
+ resources :sessions, only: [:new, :create]
+ post '/sessions/logout', to: 'sessions#logout', as: 'logout'
+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/20181017223503_create_products.rb b/db/migrate/20181017223503_create_products.rb
new file mode 100644
index 0000000000..f434bdf580
--- /dev/null
+++ b/db/migrate/20181017223503_create_products.rb
@@ -0,0 +1,11 @@
+class CreateProducts < ActiveRecord::Migration[5.2]
+ def change
+ create_table :products do |t|
+ t.string :name
+ t.float :price
+ t.string :description
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20181017223604_create_categories.rb b/db/migrate/20181017223604_create_categories.rb
new file mode 100644
index 0000000000..6ccc3914a0
--- /dev/null
+++ b/db/migrate/20181017223604_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/20181017223750_create_join_table.rb b/db/migrate/20181017223750_create_join_table.rb
new file mode 100644
index 0000000000..d5460e6b20
--- /dev/null
+++ b/db/migrate/20181017223750_create_join_table.rb
@@ -0,0 +1,7 @@
+class CreateJoinTable < ActiveRecord::Migration[5.2]
+ def change
+ create_join_table :products, :categories do |t|
+ t.index [:category_id, :product_id]
+ end
+ end
+end
diff --git a/db/migrate/20181017223753_create_order_items.rb b/db/migrate/20181017223753_create_order_items.rb
new file mode 100644
index 0000000000..a8371730a8
--- /dev/null
+++ b/db/migrate/20181017223753_create_order_items.rb
@@ -0,0 +1,10 @@
+class CreateOrderItems < ActiveRecord::Migration[5.2]
+ def change
+ create_table :order_items do |t|
+ t.integer :quantity
+ t.string :status
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20181017223934_create_orders.rb b/db/migrate/20181017223934_create_orders.rb
new file mode 100644
index 0000000000..3f283e3f1b
--- /dev/null
+++ b/db/migrate/20181017223934_create_orders.rb
@@ -0,0 +1,15 @@
+class CreateOrders < ActiveRecord::Migration[5.2]
+ def change
+ create_table :orders do |t|
+ t.string :email
+ t.string :mailing_address
+ t.integer :cc
+ t.string :cc_name
+ t.string :cc_expiration
+ t.integer :cvv
+ t.integer :zip
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20181017224245_add_order_key_to_order_item.rb b/db/migrate/20181017224245_add_order_key_to_order_item.rb
new file mode 100644
index 0000000000..140e199e1b
--- /dev/null
+++ b/db/migrate/20181017224245_add_order_key_to_order_item.rb
@@ -0,0 +1,4 @@
+class AddOrderKeyToOrderItem < ActiveRecord::Migration[5.2]
+ def change
+ end
+end
diff --git a/db/migrate/20181017225013_create_merchants.rb b/db/migrate/20181017225013_create_merchants.rb
new file mode 100644
index 0000000000..6fbed1393c
--- /dev/null
+++ b/db/migrate/20181017225013_create_merchants.rb
@@ -0,0 +1,10 @@
+class CreateMerchants < ActiveRecord::Migration[5.2]
+ def change
+ create_table :merchants do |t|
+ t.string :username
+ t.string :email
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20181018003057_add_product_id_to_order_item.rb b/db/migrate/20181018003057_add_product_id_to_order_item.rb
new file mode 100644
index 0000000000..cbbff4ef0f
--- /dev/null
+++ b/db/migrate/20181018003057_add_product_id_to_order_item.rb
@@ -0,0 +1,6 @@
+class AddProductIdToOrderItem < ActiveRecord::Migration[5.2]
+ def change
+ add_reference :products, :order_items, foreign_key: true
+ add_reference :orders, :order_items, foreign_key: true
+ end
+end
diff --git a/db/migrate/20181018003449_create_reviews.rb b/db/migrate/20181018003449_create_reviews.rb
new file mode 100644
index 0000000000..b3b1f0c445
--- /dev/null
+++ b/db/migrate/20181018003449_create_reviews.rb
@@ -0,0 +1,10 @@
+class CreateReviews < ActiveRecord::Migration[5.2]
+ def change
+ create_table :reviews do |t|
+ t.integer :rating
+ t.string :description
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20181018004819_add_foreign_key_to_review.rb b/db/migrate/20181018004819_add_foreign_key_to_review.rb
new file mode 100644
index 0000000000..e9e799df88
--- /dev/null
+++ b/db/migrate/20181018004819_add_foreign_key_to_review.rb
@@ -0,0 +1,5 @@
+class AddForeignKeyToReview < ActiveRecord::Migration[5.2]
+ def change
+ add_reference :reviews, :products, foreign_key: true
+ end
+end
diff --git a/db/migrate/20181018170301_delete_bad_fkeys_for_order_items_replace_with_order_product_id.rb b/db/migrate/20181018170301_delete_bad_fkeys_for_order_items_replace_with_order_product_id.rb
new file mode 100644
index 0000000000..2c3a2d144f
--- /dev/null
+++ b/db/migrate/20181018170301_delete_bad_fkeys_for_order_items_replace_with_order_product_id.rb
@@ -0,0 +1,9 @@
+class DeleteBadFkeysForOrderItemsReplaceWithOrderProductId < ActiveRecord::Migration[5.2]
+ def change
+ remove_reference :products, :order_items, foreign_key: true
+ remove_reference :orders, :order_items, foreign_key: true
+
+ add_reference :order_items, :products, foreign_key: true
+ add_reference :order_items, :orders, foreign_key: true
+ end
+end
diff --git a/db/migrate/20181018172853_add_merchant_id_to_products.rb b/db/migrate/20181018172853_add_merchant_id_to_products.rb
new file mode 100644
index 0000000000..afcdd3c80b
--- /dev/null
+++ b/db/migrate/20181018172853_add_merchant_id_to_products.rb
@@ -0,0 +1,5 @@
+class AddMerchantIdToProducts < ActiveRecord::Migration[5.2]
+ def change
+ add_reference :products, :merchants, foreign_key: true
+ end
+end
diff --git a/db/migrate/20181018184242_add_inventory_column_to_product.rb b/db/migrate/20181018184242_add_inventory_column_to_product.rb
new file mode 100644
index 0000000000..9b4d7cd9d6
--- /dev/null
+++ b/db/migrate/20181018184242_add_inventory_column_to_product.rb
@@ -0,0 +1,5 @@
+class AddInventoryColumnToProduct < ActiveRecord::Migration[5.2]
+ def change
+ add_column :products, :inventory, :integer
+ end
+end
diff --git a/db/migrate/20181018190315_add_description_column_to_product.rb b/db/migrate/20181018190315_add_description_column_to_product.rb
new file mode 100644
index 0000000000..00d036f8ba
--- /dev/null
+++ b/db/migrate/20181018190315_add_description_column_to_product.rb
@@ -0,0 +1,6 @@
+class AddDescriptionColumnToProduct < ActiveRecord::Migration[5.2]
+ def change
+ remove_column :products, :description, :string
+ add_column :products, :description, :text
+ end
+end
diff --git a/db/migrate/20181018190541_change_description_type_to_text_in_reviews.rb b/db/migrate/20181018190541_change_description_type_to_text_in_reviews.rb
new file mode 100644
index 0000000000..91fccaf06a
--- /dev/null
+++ b/db/migrate/20181018190541_change_description_type_to_text_in_reviews.rb
@@ -0,0 +1,6 @@
+class ChangeDescriptionTypeToTextInReviews < ActiveRecord::Migration[5.2]
+ def change
+ remove_column :reviews, :description, :string
+ add_column :reviews, :description, :text
+ end
+end
diff --git a/db/migrate/20181018205434_change_integers_in_orders_to_strings.rb b/db/migrate/20181018205434_change_integers_in_orders_to_strings.rb
new file mode 100644
index 0000000000..3d4f3cadd3
--- /dev/null
+++ b/db/migrate/20181018205434_change_integers_in_orders_to_strings.rb
@@ -0,0 +1,8 @@
+class ChangeIntegersInOrdersToStrings < ActiveRecord::Migration[5.2]
+ def change
+ change_column :orders, :cc, :string
+ change_column :orders, :cvv, :string
+ change_column :orders, :zip, :string
+ change_column :orders, :cc_expiration, :string
+ end
+end
diff --git a/db/migrate/20181018210913_fix_pluralized_foreign_keys_to_singular.rb b/db/migrate/20181018210913_fix_pluralized_foreign_keys_to_singular.rb
new file mode 100644
index 0000000000..f98a45717e
--- /dev/null
+++ b/db/migrate/20181018210913_fix_pluralized_foreign_keys_to_singular.rb
@@ -0,0 +1,10 @@
+class FixPluralizedForeignKeysToSingular < ActiveRecord::Migration[5.2]
+ def change
+ remove_reference :order_items, :products, foreign_key: true
+ remove_reference :order_items, :orders, foreign_key: true
+
+ add_reference :order_items, :product, foreign_key: true
+ add_reference :order_items, :order, foreign_key: true
+
+ end
+end
diff --git a/db/migrate/20181018212924_make_product_id_in_reviews_and_merchant_id_in_products_singular.rb b/db/migrate/20181018212924_make_product_id_in_reviews_and_merchant_id_in_products_singular.rb
new file mode 100644
index 0000000000..05ef1bda80
--- /dev/null
+++ b/db/migrate/20181018212924_make_product_id_in_reviews_and_merchant_id_in_products_singular.rb
@@ -0,0 +1,10 @@
+class MakeProductIdInReviewsAndMerchantIdInProductsSingular < ActiveRecord::Migration[5.2]
+ def change
+ remove_reference :reviews, :products, foreign_key: true
+ add_reference :reviews, :product, foreign_key: true
+
+ remove_reference :products, :merchants, foreign_key: true
+
+ add_reference :products, :merchant, foreign_key: true
+ end
+end
diff --git a/db/migrate/20181018223818_change_price_float_to_integer_in_products.rb b/db/migrate/20181018223818_change_price_float_to_integer_in_products.rb
new file mode 100644
index 0000000000..ed4eceb470
--- /dev/null
+++ b/db/migrate/20181018223818_change_price_float_to_integer_in_products.rb
@@ -0,0 +1,5 @@
+class ChangePriceFloatToIntegerInProducts < ActiveRecord::Migration[5.2]
+ def change
+ change_column :products, :price, :integer
+ end
+end
diff --git a/db/migrate/20181019023824_add_oauth_to_merchants.rb b/db/migrate/20181019023824_add_oauth_to_merchants.rb
new file mode 100644
index 0000000000..2af9b1f1df
--- /dev/null
+++ b/db/migrate/20181019023824_add_oauth_to_merchants.rb
@@ -0,0 +1,6 @@
+class AddOauthToMerchants < ActiveRecord::Migration[5.2]
+ def change
+ add_column :merchants, :provider, :string, null: false
+ add_column :merchants, :uid, :integer, null: false
+ end
+end
diff --git a/db/migrate/20181020152805_add_active_status_to_products.rb b/db/migrate/20181020152805_add_active_status_to_products.rb
new file mode 100644
index 0000000000..268720166b
--- /dev/null
+++ b/db/migrate/20181020152805_add_active_status_to_products.rb
@@ -0,0 +1,5 @@
+class AddActiveStatusToProducts < ActiveRecord::Migration[5.2]
+ def change
+ add_column :products, :is_active, :boolean, default: true
+ end
+end
diff --git a/db/migrate/20181020153054_add__status_to_orders.rb b/db/migrate/20181020153054_add__status_to_orders.rb
new file mode 100644
index 0000000000..1f61361081
--- /dev/null
+++ b/db/migrate/20181020153054_add__status_to_orders.rb
@@ -0,0 +1,5 @@
+class AddStatusToOrders < ActiveRecord::Migration[5.2]
+ def change
+ add_column :orders, :status, :string, default: 'pending'
+ end
+end
diff --git a/db/migrate/20181020154111_add_default_status_to_order_item.rb b/db/migrate/20181020154111_add_default_status_to_order_item.rb
new file mode 100644
index 0000000000..4064352183
--- /dev/null
+++ b/db/migrate/20181020154111_add_default_status_to_order_item.rb
@@ -0,0 +1,5 @@
+class AddDefaultStatusToOrderItem < ActiveRecord::Migration[5.2]
+ def change
+ change_column :order_items, :status, :string, default: 'pending'
+ end
+end
diff --git a/db/migrate/20181022220659_change_column_status_for_cart.rb b/db/migrate/20181022220659_change_column_status_for_cart.rb
new file mode 100644
index 0000000000..887f3b738e
--- /dev/null
+++ b/db/migrate/20181022220659_change_column_status_for_cart.rb
@@ -0,0 +1,5 @@
+class ChangeColumnStatusForCart < ActiveRecord::Migration[5.2]
+ def change
+ change_column :orders, :status, :string, default: 'cart'
+ end
+end
diff --git a/db/migrate/20181023011914_add_image_col_products.rb b/db/migrate/20181023011914_add_image_col_products.rb
new file mode 100644
index 0000000000..595fd5c400
--- /dev/null
+++ b/db/migrate/20181023011914_add_image_col_products.rb
@@ -0,0 +1,5 @@
+class AddImageColProducts < ActiveRecord::Migration[5.2]
+ def change
+ add_column :products, :image, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
new file mode 100644
index 0000000000..4a06155752
--- /dev/null
+++ b/db/schema.rb
@@ -0,0 +1,89 @@
+# 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_23_011914) 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", id: false, force: :cascade do |t|
+ t.bigint "product_id", null: false
+ t.bigint "category_id", null: false
+ t.index ["category_id", "product_id"], name: "index_categories_products_on_category_id_and_product_id"
+ end
+
+ create_table "merchants", force: :cascade do |t|
+ t.string "username"
+ t.string "email"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.string "provider", null: false
+ t.integer "uid", null: false
+ end
+
+ create_table "order_items", force: :cascade do |t|
+ t.integer "quantity"
+ t.string "status", default: "pending"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.bigint "product_id"
+ t.bigint "order_id"
+ 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 "mailing_address"
+ t.string "cc"
+ t.string "cc_name"
+ t.string "cc_expiration"
+ t.string "cvv"
+ t.string "zip"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.string "status", default: "cart"
+ end
+
+ create_table "products", force: :cascade do |t|
+ t.string "name"
+ t.integer "price"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.integer "inventory"
+ t.text "description"
+ t.bigint "merchant_id"
+ t.boolean "is_active", default: true
+ t.string "image"
+ t.index ["merchant_id"], name: "index_products_on_merchant_id"
+ end
+
+ create_table "reviews", force: :cascade do |t|
+ t.integer "rating"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.text "description"
+ 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..173120713d
--- /dev/null
+++ b/db/seed_data/categories.csv
@@ -0,0 +1,12 @@
+name
+toy
+book
+puzzle
+Family
+Physical
+Active
+Creative
+Dexterity
+Party
+Skilled
+Strategic
diff --git a/db/seed_data/merchants.csv b/db/seed_data/merchants.csv
new file mode 100644
index 0000000000..703ac49f00
--- /dev/null
+++ b/db/seed_data/merchants.csv
@@ -0,0 +1,9 @@
+username,email,uid,provider
+Angela,angelab@someemail.com,12234,github
+susan,susan@someemail.com,45678,github
+lula,lulasam@someemail.com,56777,github
+James,james@someemail.com,65933,github
+Samantha,samantha@someemail.com,45982,github
+Sally,sally3@someemail.com,65389,github
+Michael,m_james@someemail.com,98129,github
+David,d_smith@someemail.com,87234,github
diff --git a/db/seed_data/orders.csv b/db/seed_data/orders.csv
new file mode 100644
index 0000000000..4d08addcf4
--- /dev/null
+++ b/db/seed_data/orders.csv
@@ -0,0 +1,7 @@
+email,mailing_address,cc,cc_name,cc_expiration,cvv,zip,status
+someone@mail.com,1234 S main St Seattle WA 98134,1234567890123456,Samantha Smith,11/20,567,98134,paid
+secondone@mail.com,5432 S Republican St Seattle WA 98174,9876543212345678,Ben Tyler,11/22,765,98174,completed
+another@mail.com,5432 S Republican St Seattle WA 98174,9876543212345678,Ben Tyler,11/22,765,98174,paid
+fourthperson@mail.com,4564 S Roy St Seattle WA 98184,9876543212345678,Clementin Blue,12/22,509,98184,completed
+person_sam@mail.com,7282 S Smith St Seattle WA 98184,9876543216789654,Lula Ross,12/23,344,98184,canceled
+person_chris@mail.com,7282 N Alabama St Portland OR 94568,9876543216789654,Chris Roberts,09/23,231,94568,paid
diff --git a/db/seed_data/products.csv b/db/seed_data/products.csv
new file mode 100644
index 0000000000..067d6a3d70
--- /dev/null
+++ b/db/seed_data/products.csv
@@ -0,0 +1,12 @@
+name,price,inventory,description,image
+Tic Tac Toe,20,50,Beloved childhood game making space in a 3x3 grid. Player wins when they succeeds in placing three of their marks in horizontal vertical or diagonal row.,https://images-na.ssl-images-amazon.com/images/I/81FZ9tL%2B0QL._SX466_.jpg
+Drink-Tac-Toe,45,50,Wine beer play out the timeless classic game of tic tac toe.,https://img.papyrusonline.com/media/catalog/product/LCI/M2/Fi/YTQ1NjRlMTRmMTE5YTNkODJlMDgwN2U2N2M1YjU0OWMzNDQ5ZmYwNGZmYTA0YjdiMzlmNzAzODQyNjcyNjp7ImRzIjoiaW1hZ2UiLCJmIjoiXC84XC81XC84NTgwNjRfZC5qcGciLCJmYSI6dHJ1ZSwiZnEiOjE2LCJmdCI6dHJ1ZSwiZnciOjcyMH0~.jpg
+Otrio,50,20,This is an excellent 2-4 player head strategy game that will fire everyones competitive side.,https://target.scene7.com/is/image/Target/GUEST_4dcbcc59-1e6f-4753-ada0-69d3a5871880?wid=488&hei=488&fmt=pjpeg
+Hasbro Monopoly Classic Game,15,50,Buy sell dream and scheme your way to riches.,https://refreshcartridges.co.uk/productimages/a_126835.jpg
+Kanoodle - Brain Twisting Solitaire game,7,50,Perfect for developing problem solving and strategic thinking skills.,https://images.currentcatalog.com/catalog/product/450x450/V2978/v2978_lv811.jpg
+BrainGames,12,40,Put your brain to test with this engaging game that boasts a variety of logic challenges.,https://zcdn.freetls.fastly.net/images/cache/product//161712/zu35321091_alt_1_tm1452738711.jpg
+Flower Frenzy Puzzle,16.99,40,Enjoy this art blossom as family and friends piece together this collage of bright flowers.,https://images.currentcatalog.com/catalog/product/614552/flower-frenzy-puzzle.jpg
+Pac Man game,24.99,50,As much fun as the action packed arcade game of the same name.,https://geekandsundry.com/wp-content/uploads/2016/09/919-8ymoxNL._SL1500_-680x388.jpg
+Newtown,32.99,40,Defy the laws of grabity and add a magical twist to a familiar concept. All you gotta do is get 5 balls in a row.,https://d1whcn1ntmec99.cloudfront.net/images/catalog/products/marbles/newton/full1.jpg
+ChessGame,29.99,50,Play chess with family and friends.,https://images.unsplash.com/photo-1528819622765-d6bcf132f793?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=8cf14450e7ab6e30d85800342f9ed485&auto=format&fit=crop&w=2250&q=80
+Bean Bag Toss Game,12.29,10,stack em up and knock em down! This game is great for kids.,https://www.carnivalsavers.com/product_images/uploaded_images/can-knock-down-backyard-carnival-game-to-buy.jpg
diff --git a/db/seed_data/reviews.csv b/db/seed_data/reviews.csv
new file mode 100644
index 0000000000..0949bd0ff6
--- /dev/null
+++ b/db/seed_data/reviews.csv
@@ -0,0 +1,12 @@
+rating,description
+2,This game was not as challenging as I hoped for.
+5,I love this game. We played it as children with my siblings and it was so much fun.
+3,It was okay. I was hoping my kids would be into it but they did not enjoy it as much.
+1,It broke the minute I took it out of the packaging. It is made with cheap material.
+5,Loads of fun.
+1,Not challenging enough.
+1,Did not like it
+4,It was great
+4,My kids loved it
+1,Too expensive and not boring.
+4,Love it!
diff --git a/db/seeds.rb b/db/seeds.rb
new file mode 100644
index 0000000000..97abbd0af4
--- /dev/null
+++ b/db/seeds.rb
@@ -0,0 +1,174 @@
+require 'csv'
+
+MERCHANT_FILE = Rails.root.join('db', 'seed_data', 'merchants.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.username = row['username']
+ merchant.email = row['email']
+ merchant.uid = row['uid']
+ merchant.provider = row['provider']
+
+ successful = merchant.save
+ if !successful
+ merchant_failures << merchant
+ puts "Failed to save merchants: #{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.name = row['name']
+
+ successful = category.save
+ if !successful
+ category_failures << category
+ puts "Failed to save categories: #{category.inspect}"
+
+ else
+ puts "Created category: #{category.inspect}"
+ end
+end
+
+puts "Added #{Category.count} category records"
+puts "#{category_failures.length} categories failed to save"
+
+
+PRODUCTS_FILE = Rails.root.join('db', 'seed_data', 'products.csv')
+puts "Loading raw product data from #{PRODUCTS_FILE}"
+
+product_failures = []
+
+CSV.foreach(PRODUCTS_FILE, :headers => true) do |row|
+ product = Product.new
+
+ product.name = row['name']
+ product.price = row['price']
+ product.inventory = row['inventory']
+ product.description = row['description']
+ product.image = row['image']
+ product.merchant_id = Merchant.all.sample.id
+ product.categories << Category.all.sample(rand(1..3))
+
+
+ successful = product.save
+ if !successful
+ product_failures << product
+ puts "Failed to save product: #{product.inspect}"
+
+ else
+ puts "Created products: #{product.inspect}"
+ end
+end
+
+puts "Added #{Product.count} product records"
+puts "#{product_failures.length} products failed to save"
+
+
+ORDERS_FILE = Rails.root.join('db', 'seed_data', 'orders.csv')
+puts "Loading raw order data from #{ORDERS_FILE}"
+
+order_failures = []
+
+CSV.foreach(ORDERS_FILE, :headers => true) do |row|
+ order = Order.new
+ order.email = row['email']
+ order.mailing_address = row['mailing_address']
+ order.cc = row['cc']
+ order.cc_name = row['cc_name']
+ order.cc_expiration = row['cc_expiration']
+ order.cvv = row['cvv']
+ order.zip = row['zip']
+ order.status = row['status']
+
+ successful = order.save
+ if !successful
+ order_failures << order
+ puts "Failed to save order: #{order.inspect}"
+
+ else
+ puts "Created orders: #{order.inspect}"
+ end
+end
+
+puts "Added #{Order.count} order records"
+puts "#{order_failures.length} orders failed to save"
+
+
+
+# ORDERITEMS_FILE = Rails.root.join('db', 'seed_data', 'order_items.csv')
+# puts "Loading raw order item data from #{ORDERITEMS_FILE}"
+
+order_items_failures = []
+
+# random_orders = []
+# random_orders << Order.where.not(status:'cart')
+random_orders = Order.all.sample(5)
+
+random_orders.each do |order|
+ sample_products = Product.all.sample(rand(1..5))
+ sample_products.each do |product|
+ order_item = OrderItem.new
+ order_item.order_id = order.id
+ order_item.product_id = product.id
+ order_item.quantity = rand(1...10)
+ order_item.status = case order.status
+ when "cart"; "pending"
+ when "paid"; "paid"
+ when "completed"; "shipped"
+ end
+ successful = order_item.save
+
+ if !successful
+ order_items_failures << order_item
+ puts "Failed to save order_items: #{order_item.inspect}"
+
+ else
+ puts "Created order_item: #{order_item.inspect}"
+ end
+
+ end
+end
+
+
+REVIEWS_FILE = Rails.root.join('db', 'seed_data', 'reviews.csv')
+puts "Loading raw review data from #{REVIEWS_FILE}"
+
+review_failures = []
+
+CSV.foreach(REVIEWS_FILE, :headers => true) do |row|
+ review = Review.new
+ review.rating = row['rating']
+ review.description = row['description']
+ review.product_id = (Product.all.sample).id
+
+
+ successful = review.save
+ if !successful
+ review_failures << review
+ puts "Failed to save reviews: #{review.inspect}"
+
+ else
+ puts "Created reviews: #{review.inspect}"
+ end
+end
+
+puts "Added #{Review.count} review records"
+puts "#{review_failures.length} reviews failed to save"
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/brain.png b/public/brain.png
new file mode 100644
index 0000000000..f7f44f63bc
Binary files /dev/null and b/public/brain.png differ
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000000..fc4ce1204e
Binary files /dev/null and b/public/favicon.ico differ
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..c0cbf275e8
--- /dev/null
+++ b/test/controllers/categories_controller_test.rb
@@ -0,0 +1,65 @@
+require "test_helper"
+
+describe CategoriesController do
+ let(:one) { merchants(:one) }
+
+
+ describe "logged in users" do
+ it "should be able to access new category page" do
+ login(one)
+ get new_category_path
+ must_respond_with :success
+ end
+
+ it "should be able to create a new category with valid data" do
+ login(one)
+ data = {
+ category: {
+ name: 'zombie food'
+ }
+ }
+
+ category = Category.new(data[:category])
+ category.must_be :valid?
+ expect {
+ post categories_path, params: data
+ }.must_change('Category.count', +1)
+
+ must_respond_with :redirect
+ must_redirect_to categories_path(Category.last.id)
+ end
+
+ it "should be responded with a bad request for a non-unique category name" do
+ login(one)
+ data = {category: {name: 'toy'}}
+ category = Category.new(data[:category])
+ expect {
+ post categories_path, params: data
+ }.wont_change('Category.count')
+
+ must_respond_with :bad_request
+ end
+ end
+
+
+ describe "guest users" do
+ it "should not be able to access new category page" do
+ get new_category_path
+ must_respond_with :redirect
+ must_redirect_to root_path
+ end
+
+ it "should not be able to create a new category" do
+ data = {category: {name: 'bad category'}}
+
+ category = Category.new(data[:category])
+
+ expect {
+ post categories_path, params: data
+ }.wont_change('Category.count')
+
+ must_respond_with :redirect
+ must_redirect_to root_path
+ 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..ebe853e69c
--- /dev/null
+++ b/test/controllers/merchants_controller_test.rb
@@ -0,0 +1,14 @@
+require "test_helper"
+
+describe MerchantsController do
+ describe "show" do
+ let(:one) { merchants(:one) }
+ let(:four) { merchants(:four) }
+
+ it "goes to dashboard view when logged in" do
+ login(one)
+ get dashboard_path
+ must_respond_with :success
+ 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..2570d22d5d
--- /dev/null
+++ b/test/controllers/order_items_controller_test.rb
@@ -0,0 +1,370 @@
+require "test_helper"
+
+describe OrderItemsController do
+ let(:plushie) {products(:plushie)}
+ let(:plushie_merchant) {merchants(:one)}
+ let(:not_plushie_merchant) {merchants(:two)}
+
+ describe "Create" do
+ it "it can create an Order Item (add item to cart) for a Guest user" do
+ product = plushie
+ qty_requested = product.inventory
+ order_item_data = {
+ order_item: {
+ quantity: qty_requested,
+ product_id: product.id
+ }
+ }
+ expect {
+ post order_items_path, params: order_item_data
+ }.must_change('OrderItem.count', +1)
+
+ OrderItem.last.quantity.must_equal qty_requested
+ must_respond_with :redirect
+ must_redirect_to cart_path
+ end
+
+ it "it can create an Order Item (add item to cart) for a Merchant that doesn't have the product" do
+ login(not_plushie_merchant)
+ product = plushie
+ qty_requested = product.inventory
+ order_item_data = {
+ order_item: {
+ quantity: qty_requested,
+ product_id: product.id
+ }
+ }
+ expect {
+ post order_items_path, params: order_item_data
+ }.must_change('OrderItem.count', +1)
+
+ OrderItem.last.quantity.must_equal qty_requested
+ must_respond_with :redirect
+ must_redirect_to cart_path
+ end
+
+ it "renders 404 not_found for a bogus order_item data when trying to add to cart" do
+ order = Order.first
+ product = plushie
+
+ order_item_data = {
+ order_item: {
+ quantity: 0,
+ product_id: product.id
+ }
+ }
+
+ expect {
+ post order_items_path, params: order_item_data
+ }.wont_change('OrderItem.count')
+
+ must_redirect_to root_path
+ end
+
+ it "renders a bad request if the quantity requested is more than the product inventory" do
+ order = Order.first
+ product = plushie
+ bad_qty = product.inventory + 1
+ order_item_data = {
+ order_item: {
+ quantity: bad_qty,
+ product_id: product.id
+ }
+ }
+
+ expect {
+ post order_items_path, params: order_item_data
+ }.wont_change('OrderItem.count')
+ must_redirect_to root_path
+ end
+
+ it "updates the qty requested if the order item is already in the cart for a Guest user" do
+ product = plushie
+ qty_requested = 7
+ order_item_data = {
+ order_item: {
+ quantity: qty_requested,
+ product_id: product.id
+ }
+ }
+
+ post order_items_path, params: order_item_data
+
+ expect {
+ post order_items_path, params: order_item_data
+ }.wont_change('OrderItem.count')
+
+ OrderItem.last.quantity.must_equal qty_requested * 2
+ must_respond_with :redirect
+ must_redirect_to cart_path
+ end
+
+ it "updates the qty requested if the order item is already in the cart for a merchant who doesn't own the product" do
+ login(not_plushie_merchant)
+ product = plushie
+ qty_requested = 7
+ order_item_data = {
+ order_item: {
+ quantity: qty_requested,
+ product_id: product.id
+ }
+ }
+
+ post order_items_path, params: order_item_data
+
+ expect {
+ post order_items_path, params: order_item_data
+ }.wont_change('OrderItem.count')
+
+ OrderItem.last.quantity.must_equal qty_requested * 2
+ must_respond_with :redirect
+ must_redirect_to cart_path
+ end
+
+ it "redirects the merchant if they try to purchase their own product" do
+ login(plushie_merchant)
+ product = plushie
+ qty_requested = 7
+ order_item_data = {
+ order_item: {
+ quantity: qty_requested,
+ product_id: product.id,
+ }
+ }
+ expect {
+ post order_items_path, params: order_item_data
+ }.wont_change('OrderItem.count')
+
+ must_respond_with :redirect
+ must_redirect_to dashboard_path
+ end
+ end
+
+ describe "Update" do
+ it "successfully updates the Order Item quantity with a pending status" do
+ order_item = order_items(:pending)
+ original_qty = order_item.quantity
+ updated_qty = original_qty + 1
+
+ patch order_item_path(order_item.id), params: {
+ order_item: {
+ quantity: updated_qty
+ }
+ }
+ order_item.reload
+ order_item.quantity.must_equal updated_qty
+ must_redirect_to cart_path
+ end
+
+ it "renders a 404 not_found for bogus order item data" do
+ order_item = order_items(:pending)
+ original_qty = order_item.quantity
+
+ patch order_item_path(order_item.id), params: {
+ order_item: {
+ quantity: 'four'
+ }
+ }
+ must_redirect_to root_path
+ order_item.quantity.must_equal original_qty
+ end
+
+ it "renders a bad request if the quantity updated is more than the product inventory" do
+ order_item = order_items(:pending)
+ original_qty = order_item.quantity
+ bad_qty = original_qty + 100
+
+ patch order_item_path(order_item.id), params: {
+ order_item: {
+ quantity: bad_qty
+ }
+ }
+
+ order_item.quantity.must_equal original_qty
+ must_redirect_to root_path
+ end
+
+ it "renders a bad request if the quantity updated is good but the status is paid" do
+ paid_order_item = order_items(:paid)
+ original_qty = paid_order_item.quantity
+ good_qty = original_qty + 1
+
+ patch order_item_path(paid_order_item.id), params: {
+ order_item: {
+ quantity: good_qty
+ }
+ }
+
+ paid_order_item.quantity.must_equal original_qty
+ must_redirect_to root_path
+ end
+
+ it "renders a bad request if the quantity updated is good but the status is cancelled" do
+ paid_order_item = order_items(:cancelled)
+ original_qty = paid_order_item.quantity
+ good_qty = original_qty + 1
+
+ patch order_item_path(paid_order_item.id), params: {
+ order_item: {
+ quantity: good_qty
+ }
+ }
+
+ paid_order_item.quantity.must_equal original_qty
+ must_redirect_to root_path
+ end
+
+ it "renders a bad request if the quantity updated is good but the status is shipped" do
+ paid_order_item = order_items(:shipped)
+ original_qty = paid_order_item.quantity
+ good_qty = original_qty + 1
+
+ patch order_item_path(paid_order_item.id), params: {
+ order_item: {
+ quantity: good_qty
+ }
+ }
+
+ paid_order_item.quantity.must_equal original_qty
+ must_redirect_to root_path
+ end
+ end
+
+ describe "Update Order Item Status" do
+ it "updates the status to shipped if it has the right merchant associated with the order item" do
+ paid_order_item = order_items(:paid)
+ merchant = paid_order_item.merchant
+ login(merchant)
+ paid_order_item.status.must_equal "paid"
+
+ patch update_order_item_status_path(paid_order_item.id), params: { status: "shipped" }
+
+ paid_order_item.reload
+ paid_order_item.status.must_equal "shipped"
+ must_redirect_to dashboard_path
+ end
+
+ it "updates the status to cancelled if it has the right merchant associated with the order item" do
+ paid_order_item = order_items(:paid)
+ merchant = paid_order_item.merchant
+ login(merchant)
+ paid_order_item.status.must_equal "paid"
+
+ patch update_order_item_status_path(paid_order_item.id), params: { status: "cancelled" }
+
+ paid_order_item.reload
+ paid_order_item.status.must_equal "cancelled"
+ must_redirect_to dashboard_path
+ end
+
+ it "does not update the status if a Merchant tries to update an order item to shipped that is not associated with them" do
+ paid_order_item = order_items(:paid)
+ merchant = plushie_merchant
+ login(merchant)
+ good_qty = paid_order_item.quantity
+ paid_order_item.status.must_equal "paid"
+
+ patch update_order_item_status_path(paid_order_item.id), params: { status: "shipped" }
+
+ paid_order_item.reload
+ paid_order_item.status.must_equal "paid"
+ must_redirect_to dashboard_path
+ end
+
+ it "does not update the status if a Merchant tries to update an order item to shipped that is not associated with them" do
+ paid_order_item = order_items(:paid)
+ merchant = plushie_merchant
+ login(merchant)
+ paid_order_item.status.must_equal "paid"
+
+ patch update_order_item_status_path(paid_order_item.id), params: { status: "cancelled" }
+
+ paid_order_item.reload
+ paid_order_item.status.must_equal "paid"
+ must_redirect_to dashboard_path
+ end
+
+ it "does not update the status if a guest user tries to change the status to shipped" do
+ paid_order_item = order_items(:paid)
+ paid_order_item.status.must_equal "paid"
+
+ patch update_order_item_status_path(paid_order_item.id), params: { status: "shipped" }
+
+ paid_order_item.reload
+ paid_order_item.status.must_equal "paid"
+ must_redirect_to dashboard_path
+
+ end
+
+ it "does not update the status if a guest user tries to change the status to cancelled" do
+ paid_order_item = order_items(:paid)
+ paid_order_item.status.must_equal "paid"
+
+ patch update_order_item_status_path(paid_order_item.id), params: { status: "cancelled" }
+
+ paid_order_item.reload
+ paid_order_item.status.must_equal "paid"
+ must_redirect_to dashboard_path
+ end
+
+ it "does not update the status for a bogus status with a logged in merchant who has the product" do
+ paid_order_item = order_items(:paid)
+ merchant = paid_order_item.merchant
+ login(merchant)
+ paid_order_item.status.must_equal "paid"
+
+ patch update_order_item_status_path(paid_order_item.id), params: { status: "bogus" }
+
+ paid_order_item.reload
+ paid_order_item.status.must_equal "paid"
+ must_redirect_to dashboard_path
+ end
+ end
+
+ describe "Destroy" do
+ it "deletes an order item when status is pending" do
+ order_item = order_items(:pending)
+ order = order_item.order
+
+ expect{
+ delete order_item_path(order_item.id)
+ }.must_change('order.order_items.count', -1)
+
+ must_respond_with :redirect
+ must_redirect_to cart_path
+ end
+
+ it "does not delete order item if status is paid" do
+ order_item = order_items(:paid)
+ order = order_item.order
+
+ expect {
+ delete order_item_path(order_item.id)
+ }.wont_change('order.order_items.count')
+
+ must_redirect_to root_path
+ end
+
+ it "does not delete order item if status is cancelled" do
+ order_item = order_items(:cancelled)
+ order = order_item.order
+
+ expect {
+ delete order_item_path(order_item.id)
+ }.wont_change('order.order_items.count')
+
+ must_redirect_to root_path
+ end
+
+ it "does not delete order item if status is shipped" do
+ order_item = order_items(:shipped)
+ order = order_item.order
+
+ expect {
+ delete order_item_path(order_item.id)
+ }.wont_change('order.order_items.count')
+
+ must_redirect_to root_path
+ 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..bb8a26784e
--- /dev/null
+++ b/test/controllers/orders_controller_test.rb
@@ -0,0 +1,183 @@
+require "test_helper"
+
+describe OrdersController do
+ let(:order_one) {orders(:one)}
+ let(:order_three) {orders(:three)}
+
+ describe "index" do
+ it "should get index" do
+ get cart_path
+ must_respond_with :success
+ end
+ end
+
+ describe "show" do
+ it "should respond with success for showing an existing order with order_items " do
+ get order_path(order_three.id)
+ must_respond_with :success
+ end
+
+ it "should respond with not found for showing a non-existing order" do
+ order = orders(:two)
+
+ order.order_items.destroy_all
+ order.destroy
+
+ get order_path(order.id)
+ must_respond_with :not_found
+ end
+ end
+
+ describe "edit" do
+ it "responds with success for an existing order" do
+ product = products(:plushie)
+ order_item_data = {
+ order_item: {
+ quantity: 7,
+ product_id: product.id
+ }
+ }
+
+ post order_items_path, params: order_item_data
+ get checkout_path
+ must_respond_with :success
+ end
+
+ it "responds with error, cart is empty and redirects to root if try to edit empty cart." do
+ get checkout_path
+ must_respond_with :redirect
+ end
+ end
+
+ describe "update" do
+ before do
+ @order = orders(:four)
+ end
+
+ it "Updates the order billing params successfully" do
+ @order.email.must_equal nil
+ @order.mailing_address.must_equal nil
+ @order.cc.must_equal nil
+ @order.cc_name.must_equal nil
+ @order.cc_expiration.must_equal nil
+ @order.cvv.must_equal nil
+ @order.zip.must_equal nil
+ @order.status.must_equal 'cart'
+
+ put order_path(@order.id), params:{
+ order: {
+ email: "bbb@gmail.com",
+ mailing_address: '2334 B Street',
+ cc: 1234123412341234,
+ cc_name: 'Some Name',
+ cc_expiration: '10/23',
+ cvv: 987,
+ zip: 98123,
+ }
+ }
+
+ @order.reload
+
+ updated_order = Order.find_by(id: @order.id)
+ updated_order.email.must_equal "bbb@gmail.com"
+ updated_order.mailing_address.must_equal "2334 B Street"
+ updated_order.cc.must_equal "1234123412341234"
+ updated_order.cc_name.must_equal "Some Name"
+ updated_order.cc_expiration.must_equal "10/23"
+ updated_order.cvv.must_equal "987"
+ updated_order.zip.must_equal "98123"
+ updated_order.status.must_equal 'paid'
+ must_redirect_to order_path(@order.id)
+ end
+
+
+ it "will change the status of each order item from 'pending' to 'paid'" do
+
+ @order.order_items.each do |order_item|
+ order_item.status.must_equal 'pending'
+ end
+
+ patch order_path(@order.id), params:{
+ order: {
+ email: "bbb@gmail.com",
+ mailing_address: '2334 B Street',
+ cc: 1234123412341234,
+ cc_name: 'Some Name',
+ cc_expiration: '10/23',
+ cvv: 987,
+ zip: 98123,
+ }
+ }
+
+ @order.reload
+ @order.order_items.each do |order_item|
+ order_item.status.must_equal 'paid'
+ end
+ end
+
+ it "will change the inventory for each product placed in the order after it has been updated as paid" do
+
+ first_order_item = @order.order_items.first
+ first_product = first_order_item.product
+ original_inventory = first_product.inventory
+ requested_qty = first_order_item.quantity
+
+ patch order_path(@order.id), params:{
+ order: {
+ email: "bbb@gmail.com",
+ mailing_address: '2334 B Street',
+ cc: 1234123412341234,
+ cc_name: 'Some Name',
+ cc_expiration: '10/23',
+ cvv: 987,
+ zip: 98123,
+ }
+ }
+
+ @order.reload
+ first_order_item = @order.order_items.first
+ first_product = first_order_item.product
+ updated_inventory = first_product.inventory
+ updated_inventory.must_equal original_inventory - requested_qty
+
+ end
+
+ it "will redirect with a bad request if there is missing data" do
+ put order_path(@order.id), params:{
+ order: {
+ email: nil,
+ mailing_address: '2334 B Street',
+ cc: nil,
+ cc_name: 'Name',
+ cc_expiration: nil,
+ cvv: nil,
+ zip: nil,
+ }
+ }
+
+ updated_order = Order.find_by(id: @order.id)
+ updated_order.email.must_equal nil
+ updated_order.mailing_address.must_equal nil
+ updated_order.cc.must_equal nil
+ updated_order.cc_name.must_equal nil
+ updated_order.cc_expiration.must_equal nil
+ updated_order.cvv.must_equal nil
+ updated_order.zip.must_equal nil
+ updated_order.status.must_equal 'cart'
+
+ must_redirect_to checkout_path
+ end
+ end
+
+ describe "destroy" do
+ it "can clear the cart successfully" do
+ order = orders(:three)
+ before_order_items_count = order.order_items.count
+
+ expect {
+ delete order_path(order)
+ }.must_change('puts "inside the must_change argument"; OrderItem.count', -1)
+ must_redirect_to cart_path
+ end
+ end
+end
diff --git a/test/controllers/products_controller_test.rb b/test/controllers/products_controller_test.rb
new file mode 100644
index 0000000000..4f07c7768e
--- /dev/null
+++ b/test/controllers/products_controller_test.rb
@@ -0,0 +1,180 @@
+require "test_helper"
+
+describe ProductsController do
+ let(:one) {merchants(:one)}
+ let(:bad_product_id) {Product.first.destroy.id}
+
+ describe 'index' do
+ it "should get index for a guest" do
+ get products_path
+ must_respond_with :success
+ end
+
+ it "should get index for a logged-in user" do
+ login(one)
+ get products_path
+ must_respond_with :success
+ end
+
+ end
+
+ describe 'show' do
+ it "should get show for a guest" do
+ id = products(:puzzle)
+ get product_path(id)
+ must_respond_with :success
+ end
+
+ it "should get show for a logged-in user" do
+ login(one)
+ id = products(:puzzle)
+ get product_path(id)
+ must_respond_with :success
+ end
+
+ it "should respond with not found for showing a non-existing product for a guest" do
+ get product_path(bad_product_id)
+ must_respond_with :not_found
+ end
+
+ it "should respond with not found for showing a non-existing product for a logged-in user" do
+ login(one)
+ get product_path(bad_product_id)
+ must_respond_with :not_found
+ end
+ end
+
+ describe 'new' do
+ it 'should get a new form to add products' do
+ login(one)
+ get new_merchant_product_path(one.id)
+ must_respond_with :success
+ end
+ end
+
+ describe 'create' do
+ it 'it can create a product with valid data' do
+ login(one)
+ product_data = {
+ product: {
+ name: "some toy",
+ price: 25,
+ inventory: 10,
+ merchant_id: one.id,
+ description: "An integlligent brain plushie",
+ category_ids: [categories(:toy).id]
+ }
+ }
+
+ test_product = Product.new(product_data[:product])
+ valid = test_product.valid?
+ valid.must_equal true, "Product data was invalid: #{test_product.errors.messages}, please come fix this test"
+
+ expect{
+ post merchant_products_path(one.id), params: product_data
+ }.must_change('Product.count', +1)
+
+ must_redirect_to product_path(Product.last)
+ end
+
+ it 'does not create a new product with invalid data' do
+ login(one)
+
+ product_data = {
+ product: {
+ merchant: merchants(:one),
+ category_ids: [categories(:toy).id]
+ }
+ }
+
+ Product.new(product_data[:product]).wont_be :valid?
+
+ expect {
+ post(merchant_products_path(one.id), params: product_data)
+ }.wont_change('Product.count')
+
+ must_respond_with :bad_request
+ end
+ end
+
+ describe "edit" do
+ it "responds with success for an existing book" do
+ login(one)
+ product = Product.first
+ get edit_merchant_product_path(one.id, product.id)
+ must_respond_with :success
+ end
+
+ it "responds with not_found for a book that D.N.E." do
+ login(one)
+ get edit_merchant_product_path(one.id, bad_product_id)
+ must_respond_with :not_found
+ end
+ end
+
+ describe 'update' do
+ it "updates with valid data with a logged-in user" do
+ login(one)
+
+ existing_product = products(:puddy)
+
+ put merchant_product_path(one.id, existing_product.id), params: {
+ product: {
+ name: "Brain Puddy UPDATED",
+ price: 14,
+ description: "Updated Description!",
+ inventory: 100
+ }
+ }
+
+ updated_product = Product.find_by(id: existing_product.id)
+
+ updated_product.name.must_equal "Brain Puddy UPDATED"
+ must_respond_with :redirect
+ must_redirect_to product_path(updated_product.id)
+ end
+
+ it "fails for bogus data and aproduct with a logged-in user" do
+ login(one)
+
+ existing_product = products(:puddy)
+
+ put merchant_product_path(one.id, existing_product.id), params: {
+ product: {
+ name: "",
+ price: 14,
+ description: "Updated Description!",
+ inventory: 100
+ }
+ }
+
+ unchanged_product = Product.find_by(id: existing_product.id)
+
+ unchanged_product.name.must_equal "Brain Puddy"
+ must_respond_with :bad_request
+ end
+ end
+
+ describe 'change product status' do
+ it 'it should change the status with post request' do
+ login(one)
+
+ existing_product = products(:puddy)
+
+ post change_status_merchant_product_path(one.id, existing_product.id), params: {
+ product: {
+ name: "Brain Puddy",
+ price: 14,
+ description: "Description!",
+ inventory: 100,
+ is_active: false
+ }
+ }
+
+ updated_product = Product.find_by(id: existing_product.id)
+
+ updated_product.is_active.must_equal false
+ must_redirect_to dashboard_path
+ 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..da7d3b02d7
--- /dev/null
+++ b/test/controllers/reviews_controller_test.rb
@@ -0,0 +1,110 @@
+require "test_helper"
+
+describe ReviewsController do
+ let(:one) { merchants(:one)}
+ let (:two) {merchants(:two)}
+ let(:plushie) { products(:plushie) }
+
+ describe "new" do
+ describe "logged in users" do
+ it "get to a new review page" do
+ login(one)
+ get new_product_review_path(plushie.id)
+ must_respond_with :success
+ end
+
+ it "can't get to a new review page for non-existent product" do
+ login(one)
+ plushie.destroy
+ get new_product_review_path(plushie.id)
+ must_respond_with :not_found
+ end
+ end
+
+ describe "guest users" do
+ it "can get to a new review page" do
+ get new_product_review_path(plushie.id)
+ must_respond_with :success
+ end
+
+ it "can't get to a new review page for non-existent product" do
+ plushie.destroy
+ get new_product_review_path(plushie.id)
+ must_respond_with :not_found
+ end
+ end
+
+ end
+
+ describe "create" do
+ let (:good_data) { {review: {rating: 5, product: plushie, description: 'luv it!!'} } }
+ let (:bad_data) { {review: {rating: -5, product: plushie, description: 'hate it!! :( '} } }
+ let (:bad_data2) { {review: {rating: 1, product: plushie, description: ''} } }
+
+ describe "logged in users" do
+ it "can submit review for products they don't sell" do
+ login(two)
+ expect{
+ post product_reviews_path(plushie.id), params: good_data
+ }.must_change('Review.count', +1)
+
+ must_redirect_to product_path(plushie.id)
+
+ end
+
+ it "can't submit review with invalid rating" do
+ login(two)
+ expect{
+ post product_reviews_path(plushie.id), params: bad_data
+ }.wont_change('Review.count')
+ must_respond_with :bad_request
+ end
+
+ it "can't submit review with invalid description" do
+ login(two)
+ expect{
+ post product_reviews_path(plushie.id), params: bad_data2
+ }.wont_change('Review.count')
+ must_respond_with :bad_request
+ end
+
+ it "can't submit a review for products they sell" do
+ login(one)
+
+ expect{
+ post product_reviews_path(plushie.id), params: good_data
+ }.wont_change('Review.count')
+
+ must_respond_with :redirect
+ must_redirect_to dashboard_path
+
+
+ end
+ end
+
+ describe "guest users" do
+ it "guest users can submit review" do
+ expect{
+ post product_reviews_path(plushie.id), params: good_data
+ }.must_change('Review.count', +1)
+
+ must_redirect_to product_path(plushie.id)
+ end
+
+ it "can't submit review with invalid rating" do
+ expect{
+ post product_reviews_path(plushie.id), params: bad_data
+ }.wont_change('Review.count')
+ must_respond_with :bad_request
+ end
+
+ it "can't submit review with invalid description" do
+ expect{
+ post product_reviews_path(plushie.id), params: bad_data2
+ }.wont_change('Review.count')
+ must_respond_with :bad_request
+ end
+ end
+
+ end
+end
diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb
new file mode 100644
index 0000000000..5a4f757d2e
--- /dev/null
+++ b/test/controllers/sessions_controller_test.rb
@@ -0,0 +1,85 @@
+require "test_helper"
+
+describe SessionsController do
+
+ describe "login" do
+ it "can successfully log in with githun as an exisiting user and redirects to root path" do
+
+ #Arrange
+ # Make sure that for some existing user, everything is configured
+
+ merchant_one = merchants(:one)
+
+ OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new( mock_auth_hash( merchant_one ) )
+
+ #Act
+ # Simulating logging in with github being successful (given the OmniAUth hash above)
+ get auth_callback_path(:github)
+
+ # Assert
+ #Check that it redirects
+ must_redirect_to root_path
+
+
+ expect(flash[:success]).must_equal "Logged in as returning user #{merchant_one.username}"
+ end
+
+ it 'creates a new user successfully when logging in with a new valid merchant' do
+
+ start_merch_count = Merchant.count
+
+ new_merchant = Merchant.new(username: "nicodimos", uid: 56, provider: :github, email: "nico@zzz.com")
+
+ expect(new_merchant.valid?).must_equal true
+
+ OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new( mock_auth_hash( new_merchant) )
+
+ get auth_callback_path(:github)
+
+ must_redirect_to root_path
+ expect( Merchant.count ).must_equal start_merch_count + 1
+ expect( session[:merchant_id] ).must_equal Merchant.last.id
+ expect( flash[:success] ).must_equal "Logged in Successfully. Welcome #{new_merchant.username}"
+ end
+
+ it 'does not create a new merchant when logging in with a new invalid merchant' do
+
+ start_merch_count = Merchant.count
+
+ invalid_new_merchant = Merchant.new(username: nil, uid: nil, provider: :github, email: "nico@zzz.com")
+
+ expect(invalid_new_merchant.valid?).must_equal false
+
+ OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new( mock_auth_hash( invalid_new_merchant ) )
+
+ get auth_callback_path(:github)
+
+ must_redirect_to root_path
+ expect( Merchant.count ).must_equal start_merch_count
+ expect( session[:merchant_id] ).must_equal nil
+ expect( flash[:error] ).must_equal "Could not create new account: #{invalid_new_merchant.errors.messages}"
+ end
+ end
+ describe 'logout' do
+ before do
+ merchant_one = merchants(:one)
+
+ OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new( mock_auth_hash( merchant_one ) )
+
+ get auth_callback_path(:github)
+ end
+ it 'can successfully logout by clearing the session' do
+
+ post logout_path(session[:merchant_id])
+
+ expect( session[:merchant_id]).must_equal nil
+ expect( flash[:success] ).must_equal "Successfully logged out"
+ end
+
+ it 'should redirect to the root page' do
+ post logout_path(session[:merchant_id])
+
+ must_redirect_to root_path
+ 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..9a5d032e34
--- /dev/null
+++ b/test/fixtures/categories.yml
@@ -0,0 +1,13 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+toy:
+ name: toy
+
+book:
+ name: book
+
+meditation:
+ name: meditation
+
+apparel:
+ name: apparel
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..2cb3fe49a1
--- /dev/null
+++ b/test/fixtures/merchants.yml
@@ -0,0 +1,25 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ username: griffiam
+ email: sesamestreet@yahoo.com
+ uid: 12345678
+ provider: github
+
+two:
+ username: yggurlie
+ email: yggurlie@yahoo.com
+ uid: 87654321
+ provider: github
+
+three:
+ username: maddiemax
+ email: maddiemaxwell@yahoo.com
+ uid: 23456789
+ provider: github
+
+four:
+ username: boing
+ email: boing@yahoo.com
+ uid: 23456732
+ provider: github
diff --git a/test/fixtures/order_items.yml b/test/fixtures/order_items.yml
new file mode 100644
index 0000000000..5e38978584
--- /dev/null
+++ b/test/fixtures/order_items.yml
@@ -0,0 +1,43 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+pending:
+ quantity: 1
+ status: pending
+ order: three
+ product: plushie
+
+paid:
+ quantity: 1
+ status: paid
+ order: two
+ product: puzzle
+
+shipped:
+ quantity: 1
+ status: shipped
+ order: one
+ product: puddy
+
+cancelled:
+ quantity: 1
+ status: cancelled
+ order: two
+ product: plushie
+
+many:
+ quantity: 20
+ status: pending
+ order: two
+ product: puzzle
+
+paid2:
+ quantity: 10
+ status: paid
+ order: two
+ product: puzzle
+
+pending2:
+ quantity: 5
+ status: pending
+ order: four
+ product: yoga
diff --git a/test/fixtures/orders.yml b/test/fixtures/orders.yml
new file mode 100644
index 0000000000..321d562cad
--- /dev/null
+++ b/test/fixtures/orders.yml
@@ -0,0 +1,37 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ email: aaa@gmail.com
+ mailing_address: 12345 A Street
+ cc: 1234123412341234
+ cc_name: Aaa Aaaaaa
+ cc_expiration: 10/22
+ cvv: 123
+ zip: 12345
+ status: paid
+
+two:
+ email: z@zmail.com
+ mailing_address: 90 Z Ave
+ cc: 1111222233334444
+ cc_name: Zebra Zee
+ cc_expiration: 12/20
+ cvv: 100
+ zip: 50100
+ status: completed
+
+three:
+ email: nil
+ mailing_address: nil
+ cc: nil
+ cc_name: nil
+ cc_expiration: nil
+ cvv: nil
+ zip: nil
+ status: cart
+
+four:
+ status: cart
+
+five:
+ status: cart
diff --git a/test/fixtures/products.yml b/test/fixtures/products.yml
new file mode 100644
index 0000000000..e409de2b01
--- /dev/null
+++ b/test/fixtures/products.yml
@@ -0,0 +1,38 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+plushie:
+ name: Brain Plushie
+ price: 15
+ description: A beautiful integlligent brain plushie
+ inventory: 10
+ merchant: one
+ categories: toy
+ is_active: true
+
+puzzle:
+ name: Brain Puzzle
+ price: 100
+ description: improve ur mind!
+ merchant: two
+ inventory: 2
+ categories: toy
+ is_active: true
+
+puddy:
+ name: Brain Puddy
+ price: 14
+ description: puddy for ur brain!
+ inventory: 5
+ merchant: two
+ categories: toy
+ is_active: true
+
+
+yoga:
+ name: Yoga mat
+ price: 475
+ description: puddy for ur brain!
+ inventory: 100
+ merchant: three
+ categories: meditation, apparel
+ is_active: true
diff --git a/test/fixtures/reviews.yml b/test/fixtures/reviews.yml
new file mode 100644
index 0000000000..79b595d03e
--- /dev/null
+++ b/test/fixtures/reviews.yml
@@ -0,0 +1,16 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ rating: 5
+ product: puddy
+ description: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
+
+two:
+ rating: 3
+ product: plushie
+ description: Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
+
+three:
+ rating: 1
+ product: plushie
+ description: Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
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..5e9af8da53
--- /dev/null
+++ b/test/models/category_test.rb
@@ -0,0 +1,34 @@
+require "test_helper"
+
+describe Category do
+
+ describe 'validations' do
+ before do
+ @category = Category.new(name: 'candies')
+ end
+
+ it 'is valid when name is present and unique' do
+ result = @category.valid?
+ expect( result ).must_equal true
+ end
+
+
+ it 'is invalid without a name' do
+ @category.name = nil
+
+ result = @category.valid?
+
+ expect( result ).wont_equal true
+ expect( @category.errors.messages ).must_include :name
+ end
+
+ it 'is invalid with a non-unique name' do
+ @category = Category.new(name: 'toy')
+ result = @category.valid?
+
+ expect( result ).wont_equal true
+ expect( @category.errors.messages ).must_include :name
+ end
+
+ end
+end
diff --git a/test/models/merchant_test.rb b/test/models/merchant_test.rb
new file mode 100644
index 0000000000..e43134d866
--- /dev/null
+++ b/test/models/merchant_test.rb
@@ -0,0 +1,75 @@
+require "test_helper"
+
+describe Merchant do
+ describe 'relations' do
+ it 'has many products' do
+ merchant = Merchant.first
+ merchant.products.each do |product|
+ product.must_be_instance_of Product
+ end
+ end
+ end
+
+
+ describe 'revenue' do
+ let(:two) {merchants(:two)}
+ let(:four) {merchants(:four)}
+ let(:three) {merchants(:three)}
+
+ it 'calculates revenue for merchant with many products / order items' do
+ expect(two.revenue).must_equal 1114.00
+ end
+
+ it 'calculates revenue for merchant with no products' do
+ expect(four.revenue).must_equal 0.0
+ end
+
+ it 'calculates revenue for merchant with product / no order item ' do
+ expect(three.revenue).must_equal 0.0
+ end
+
+
+ end
+
+ describe 'validations' do
+ before do
+ @merchant = Merchant.new(
+ username: 'test_merchant',
+ email: 'test_merchant@braineaze.com'
+ )
+ end
+
+ it 'is valid when username is present and unique and email is present and unique' do
+ is_valid = @merchant.valid?
+ expect( is_valid ).must_equal true
+ end
+
+ it 'is invalid without a username' do
+ @merchant.username = nil
+ is_valid = @merchant.valid?
+ expect( is_valid ).must_equal false
+ expect( @merchant.errors.messages ).must_include :username
+ end
+
+ it 'is invalid without a email' do
+ @merchant.email = nil
+ is_valid = @merchant.valid?
+ expect( is_valid ).must_equal false
+ expect( @merchant.errors.messages ).must_include :email
+ end
+
+ it 'is invalid with a non-unique username' do
+ @merchant.username = Merchant.first.username
+ is_valid = @merchant.valid?
+ expect( is_valid ).must_equal false
+ expect( @merchant.errors.messages ).must_include :username
+ end
+
+ it 'is invalid with a non-unique email' do
+ @merchant.email = Merchant.first.email
+ is_valid = @merchant.valid?
+ expect( is_valid ).must_equal false
+ expect( @merchant.errors.messages ).must_include :email
+ 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..a98b906772
--- /dev/null
+++ b/test/models/order_item_test.rb
@@ -0,0 +1,95 @@
+require "test_helper"
+
+describe OrderItem do
+ describe 'relations' do
+ before do
+ @order_item = OrderItem.first
+ end
+
+ it "belongs to a product" do
+ product = @order_item.product
+
+ expect( product ).must_be_kind_of Product
+ end
+
+ it "belongs to an order" do
+ @order_item = OrderItem.first
+
+ order = @order_item.order
+
+ expect( order ).must_be_kind_of Order
+ end
+ end
+
+ describe 'validations' do
+ before do
+ @order_item = OrderItem.first
+ end
+
+ it 'is valid with product_id present' do
+ result = @order_item.valid?
+ expect( result ).must_equal true
+ end
+
+
+ it 'is invalid without product_id' do
+ @order_item.product_id = nil
+ result = @order_item.valid?
+
+ expect( result ).wont_equal true
+ expect( @order_item.errors.messages ).must_include :product
+ end
+
+ it 'is valid with order_id present' do
+ result = @order_item.valid?
+
+ expect( result ).must_equal true
+ end
+
+ it 'is invalid without order_id' do
+ @order_item.order_id = nil
+ result = @order_item.valid?
+
+ expect( result ).wont_equal true
+ expect( @order_item.errors.messages ).must_include :order
+ end
+
+ it 'is invalid without status' do
+ @order_item.status = nil
+ result = @order_item.valid?
+
+ expect( result ).wont_equal true
+ expect( @order_item.errors.messages ).must_include :status
+ end
+
+ it 'is valid with status' do
+ result = @order_item.valid?
+ expect( result ).must_equal true
+ end
+
+ it 'is invalid if quantity is 0' do
+ @order_item.quantity = 0
+ result = @order_item.valid?
+
+ expect( result ).wont_equal true
+ expect( @order_item.errors.messages ).must_include :quantity
+ end
+
+ it 'is invalid if quantity is nil' do
+ @order_item.quantity = nil
+ result = @order_item.valid?
+
+ expect( result ).wont_equal true
+ expect( @order_item.errors.messages ).must_include :quantity
+ end
+
+ # it 'is invalid if quantity is greater than product inventory' do
+ # @order_item.quantity = 20
+ # result = @order_item.valid?
+ #
+ # expect( result ).wont_equal true
+ # expect( @order_item.errors.messages ).must_include :quantity
+ # end
+ end
+
+end
diff --git a/test/models/order_test.rb b/test/models/order_test.rb
new file mode 100644
index 0000000000..9f4102abad
--- /dev/null
+++ b/test/models/order_test.rb
@@ -0,0 +1,81 @@
+require "test_helper"
+
+describe Order do
+
+ describe 'relations' do
+ it "has a list of order items" do
+ order = orders(:one)
+ order.must_respond_to :order_items
+
+ order.order_items.each do |order_item|
+ order_item.must_be_kind_of OrderItem
+ end
+ end
+ end
+
+ describe 'validations' do
+ before do
+ @order = orders(:one)
+ end
+
+ it 'is valid when email, mailing, cc, cc_name, ccv and zip is present' do
+ is_valid = @order.valid?
+ expect( is_valid ).must_equal true
+ end
+
+ it 'is invalid when email is missing' do
+ @order.email = nil
+
+ is_valid = @order.valid?
+
+ expect( is_valid ).must_equal false
+
+ expect( @order.errors.messages ).must_include :email
+ end
+
+ it 'is invalid when mailing address is missing' do
+ @order.mailing_address = nil
+
+ is_valid = @order.valid?
+
+ expect( is_valid ).must_equal false
+ expect( @order.errors.messages ).must_include :mailing_address
+ end
+
+ it 'is invalid when cc is missing' do
+ @order.cc = nil
+
+ is_valid = @order.valid?
+
+ expect( is_valid ).must_equal false
+ expect( @order.errors.messages ).must_include :cc
+ end
+
+ it 'is invalid when cc name is missing' do
+ @order.cc_name = nil
+
+ is_valid = @order.valid?
+
+ expect( is_valid ).must_equal false
+ expect( @order.errors.messages ).must_include :cc_name
+ end
+
+ it 'is invalid when cvv name is missing' do
+ @order.cvv = nil
+
+ is_valid = @order.valid?
+
+ expect( is_valid ).must_equal false
+ expect( @order.errors.messages ).must_include :cvv
+ end
+
+ it 'is invalid when zip is missing' do
+ @order.zip = nil
+
+ is_valid = @order.valid?
+
+ expect( is_valid ).must_equal false
+ expect( @order.errors.messages ).must_include :zip
+ end
+ end
+end
diff --git a/test/models/product_test.rb b/test/models/product_test.rb
new file mode 100644
index 0000000000..7701faa65f
--- /dev/null
+++ b/test/models/product_test.rb
@@ -0,0 +1,110 @@
+require "test_helper"
+
+describe Product do
+ describe 'relations' do
+ it 'has many reviews' do
+ product = Product.first
+ product.reviews.each do |review|
+ review.must_be_instance_of Review
+ end
+ end
+
+ it 'has many order_items' do
+ product = Product.first
+ product.order_items.each do |order_item|
+ order_item.must_be_instance_of OrderItem
+ end
+ end
+
+ it 'has many categories' do
+ product = Product.first
+ product.categories.each do |category|
+ category.must_be_instance_of Category
+ end
+ end
+
+ it 'belongs to a Merchant' do
+ plushie = products(:plushie)
+ plushie.must_respond_to :merchant
+ plushie.merchant.must_be_kind_of Merchant
+ plushie.merchant.username.must_equal "griffiam"
+ plushie.merchant.must_equal merchants(:one)
+ end
+ end
+
+ describe 'currency' do
+ it 'converts price to float' do
+ products(:plushie).currency.must_equal 15.00
+ end
+ end
+
+ describe 'change inventory method' do
+ it 'changes inventory stock on a product' do
+ products(:plushie).change_inventory(2)
+ products(:plushie).inventory.must_equal 8
+ end
+ end
+
+
+ describe 'validations' do
+ before do
+ @product = Product.new(
+ name: 'test_product',
+ price: 1005,
+ inventory: 4
+ )
+ end
+
+ it 'is valid when name is present and unique, price is present, and inventory is present and greater or equal to 0' do
+ plushie = products(:plushie)
+ plushie.valid?.must_equal true
+ end
+
+ it 'is invalid without a name' do
+ @product.name = nil
+ is_valid = @product.valid?
+ expect( is_valid ).must_equal false
+ expect( @product.errors.messages ).must_include :name
+ end
+
+ it 'is invalid without a price' do
+ @product.price = nil
+ is_valid = @product.valid?
+ expect( is_valid ).must_equal false
+ expect( @product.errors.messages ).must_include :price
+ end
+
+ it 'is invalid if price is less or equal to 0' do
+ @product.price = 0
+ is_valid = @product.valid?
+ expect( is_valid ).must_equal false
+ expect( @product.errors.messages ).must_include :price
+
+ @product.price = -2
+ is_valid = @product.valid?
+ expect( is_valid ).must_equal false
+ expect( @product.errors.messages ).must_include :price
+ end
+
+ it 'is invalid without an inventory amt' do
+ @product.inventory = nil
+ is_valid = @product.valid?
+ expect( is_valid ).must_equal false
+ expect( @product.errors.messages ).must_include :inventory
+ end
+
+ it 'is invalid if inventory amt is less than 0' do
+ @product.inventory = -1
+ is_valid = @product.valid?
+ expect( is_valid ).must_equal false
+ expect( @product.errors.messages ).must_include :inventory
+ end
+
+ it 'is invalid with a non-unique name' do
+ @product.name = Product.first.name
+ is_valid = @product.valid?
+ expect( is_valid ).must_equal false
+ expect( @product.errors.messages ).must_include :name
+ end
+ end
+end
diff --git a/test/models/review_test.rb b/test/models/review_test.rb
new file mode 100644
index 0000000000..7ec2105794
--- /dev/null
+++ b/test/models/review_test.rb
@@ -0,0 +1,72 @@
+require "test_helper"
+
+describe Review do
+ describe 'relations' do
+ it "belongs to a product" do
+ review = Review.first
+ product = review.product
+ product.must_be_instance_of Product
+ end
+ end
+
+ describe 'validations' do
+ before do
+ @review = Review.new(
+ product_id: Product.first.id,
+ rating: 5,
+ description: 'test-description'
+ )
+ end
+
+ it 'is invalid without a rating' do
+ @review.rating = nil
+ result = @review.valid?
+
+ expect( result ).wont_equal true
+ end
+
+ it 'is invalid if rating is not an integer' do
+ @review.rating = 'five'
+
+ result = @review.valid?
+ expect( result ).wont_equal true
+ end
+
+ it 'is valid when rating is between 1 and 5' do
+ result = @review.valid?
+ expect( result ).must_equal true
+ end
+
+ it 'is invalid when rating is less than 1' do
+ @review.rating = -1
+ result = @review.valid?
+
+ expect( result ).wont_equal true
+ end
+
+ it 'is invalid when rating is greater than 5' do
+ @review.rating = 6
+ result = @review.valid?
+
+ expect( result ).wont_equal true
+ end
+
+ it 'is valid with product_id present' do
+ result = @review.valid?
+ expect( result ).must_equal true
+ end
+
+ it 'is valid with a description' do
+ result = @review.valid?
+ expect( result ).must_equal true
+ end
+
+ it 'is invalid without a description' do
+ @review.description = nil
+ result = @review.valid?
+
+ expect( result ).wont_equal true
+ 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..d98b637e9a
--- /dev/null
+++ b/test/test_helper.rb
@@ -0,0 +1,56 @@
+require 'simplecov'
+
+SimpleCov.start 'rails' do
+ add_filter '/bin/'
+ add_filter '/db/'
+ add_filter '/test/'
+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
+ # Once you have enabled test mode, all requests
+ # to OmniAuth will be short circuited to use the mock authentication hash.
+ # A request to /auth/provider will redirect immediately to /auth/provider/callback.
+ OmniAuth.config.test_mode = true
+ end
+
+ def mock_auth_hash(user)
+ return {
+ uid: user.uid,
+ provider: user.provider,
+ info: {
+ nickname: user.username,
+ email: user.email
+ }
+ }
+ end
+
+ def login(user)
+ OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new(mock_auth_hash(user))
+ get auth_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