diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..82e49ead9 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,72 @@ +jobs: + # Project has lots of rubocop fixing to do. Enable this once fixed in separate pr. + # rubocop: + # docker: + # - image: 'cimg/ruby:3.0.3' + # steps: + # - checkout + # - ruby/install-deps + # - ruby/rubocop-check: + # format: progress + # label: Inspecting with Rubocop + + test: + docker: + - image: 'cimg/ruby:3.0.3-browsers' + - image: 'circleci/postgres:12.0-alpine-ram' + environment: + POSTGRES_DB: choco_cake_test + POSTGRES_PASSWORD: password + POSTGRES_USER: choco + - image: 'circleci/redis:6.2-alpine' + + environment: + BUNDLE_JOBS: '3' + BUNDLE_RETRY: '3' + PAGER: '' + POSTGRES_DB: choco_cake_test + POSTGRES_PASSWORD: password + POSTGRES_USER: choco + POSTGRES_HOST: "127.0.0.1" + RAILS_ENV: test + steps: + - checkout + # - browser-tools/install-firefox + - ruby/install-deps + - run: + command: 'dockerize -wait tcp://localhost:5432 -timeout 1m' + name: Wait for DB + - run: + command: 'sudo apt-get update && sudo apt-get install --yes --no-install-recommends postgresql-client jq curl imagemagick' + name: Install postgres client, jq, curl, imagemagick + - run: + command: 'bin/rails db:setup --trace' + name: Database setup + - ruby/rspec-test + - store_artifacts: + path: coverage + - run: + name: Post test coverage to Github + command: bash -ue .circleci/record_coverage + when: always + +orbs: + browser-tools: circleci/browser-tools@1 + node: circleci/node@4 + ruby: circleci/ruby@1.3 + +version: 2.1 + +workflows: + # code_quality: + # jobs: + # - rubocop: + # filters: + # branches: + # ignore: + # - master + # - main + test: + jobs: + - test: + context: raspberrypigithubbot diff --git a/.circleci/record_coverage b/.circleci/record_coverage new file mode 100644 index 000000000..9a60a0d4c --- /dev/null +++ b/.circleci/record_coverage @@ -0,0 +1,103 @@ +#!/bin/bash -ueo pipefail + +# Record coverage +# +# This script uses the Circle and Github APIs to poke a comment into a PR about test coverage. +# +# To work, the GITHUB_TOKEN and CIRCLE_TOKEN vars must be in the environment, +# with appropriate API tokens from GH and Circle. +# +# Also to get the magic link to your test coverage, you'll want to store the +# `coverage/` directory. +#``` +# - store_artifacts: +# path: coverage +#``` + +CURL_ARGS="-s -S -f" + +function graceful_exit() { + echo "*** Something failed! Exiting gracefully so the build doesn't fail overall" + exit 0 +} + +# +# Wrapper for the Github GraphQL API +# +function gh_query() { + # Build and escape our JSON + json=$(jq -n --arg q "$*" '{query: $q}') + curl $CURL_ARGS -H "Authorization: bearer $GITHUB_TOKEN" -X POST -d "$json" https://api.github.com/graphql +} + + +# Trap any fails, and force a successful exit. +trap graceful_exit ERR + +last_run=coverage/.last_run.json +if ! [ -s $last_run ] ; then + echo "*** No $last_run file found." + exit 0 +fi + +which jq > /dev/null || sudo apt-get install -y jq + +# This is the message that makes it into github +msg="* CircleCI build [#${CIRCLE_BUILD_NUM}](${CIRCLE_BUILD_URL})\n" +msg="$msg* Test coverage: " + + +# Check to see if coverage is under `result.line` or under `result.covered_percent` (older versions) +coverage=$(jq -r 'if .result.line then .result.line else .result.covered_percent end' < coverage/.last_run.json) + +if [ "${coverage}" = "null" ] ; then + echo "*** Failed to determine coverage" + exit 0 +fi + +artifacts_response=$(curl $CURL_ARGS -H "Circle-Token: $CIRCLE_TOKEN" https://circleci.com/api/v1.1/project/gh/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/${CIRCLE_BUILD_NUM}/artifacts) +coverage_url=$(echo ${artifacts_response} | jq -r '. | map(select(.path == "coverage/index.html"))[0].url') + +if ! [ "${coverage_url}" = "null" ] ; then + msg="$msg [$coverage%]($coverage_url)\n\n" +else + msg="$msg $coverage%\n\n" + msg="$msg > CircleCI didn't store the Simplecov index (maybe the store_artifacts step is missing?)" +fi + +# Find associated PR. *NB* we're assuming that the first, open PR is the one +# to comment on. +q="query { + repository(name: \"${CIRCLE_PROJECT_REPONAME}\", owner: \"${CIRCLE_PROJECT_USERNAME}\") { + ref(qualifiedName: \"${CIRCLE_BRANCH}\") { + associatedPullRequests(first: 1) { + nodes { + id + } + } + } + } +}" + +pr_response=$(gh_query $q) +pr_node=$(echo $pr_response | jq -r ".data.repository.ref.associatedPullRequests.nodes[0].id") + +if [ "$pr_node" = "null" ] ; then + echo "*** No PR found" + exit 0 +fi + + +echo ">>> Posting code coverage comment" +m="mutation { + addComment(input: { + subjectId: \"${pr_node}\", + body: \"${msg}\" + }) { + subject { + id + } + } +}" + +gh_query $m diff --git a/.env.example b/.env.example index a4c5cba9a..f11568174 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,4 @@ ALLOWED_ORIGINS=localhost:3002,localhost:3000 +POSTGRES_HOST=changeme +POSTGRES_USER=changeme +POSTGRES_PASSWORD=changeme diff --git a/Gemfile b/Gemfile index 9e00b3024..d7b391af2 100644 --- a/Gemfile +++ b/Gemfile @@ -18,8 +18,11 @@ group :development, :test do gem 'dotenv-rails' gem 'factory_bot_rails' gem 'faker' + gem 'rspec' + gem 'rspec_junit_formatter' gem 'rspec-rails' gem 'rubocop' + gem 'simplecov', require: false end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 015db9226..ca7e1b59c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -67,6 +67,7 @@ GEM concurrent-ruby (1.1.9) crass (1.0.6) diff-lcs (1.4.4) + docile (1.4.0) dotenv (2.7.6) dotenv-rails (2.7.6) dotenv (= 2.7.6) @@ -103,6 +104,8 @@ GEM nokogiri (1.12.5) mini_portile2 (~> 2.6.1) racc (~> 1.4) + nokogiri (1.12.5-x86_64-darwin) + racc (~> 1.4) nokogiri (1.12.5-x86_64-linux) racc (~> 1.4) parallel (1.21.0) @@ -147,6 +150,10 @@ GEM rake (13.0.6) regexp_parser (2.2.0) rexml (3.2.5) + rspec (3.10.0) + rspec-core (~> 3.10.0) + rspec-expectations (~> 3.10.0) + rspec-mocks (~> 3.10.0) rspec-core (3.10.1) rspec-support (~> 3.10.0) rspec-expectations (3.10.1) @@ -164,6 +171,8 @@ GEM rspec-mocks (~> 3.10) rspec-support (~> 3.10) rspec-support (3.10.3) + rspec_junit_formatter (0.5.1) + rspec-core (>= 2, < 4, != 2.12.0) rubocop (1.23.0) parallel (~> 1.10) parser (>= 3.0.0.0) @@ -178,6 +187,12 @@ GEM ruby-progressbar (1.11.0) shoulda-matchers (5.0.0) activesupport (>= 5.2.0) + simplecov (0.21.2) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.12.3) + simplecov_json_formatter (0.1.3) sprockets (4.0.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -201,6 +216,7 @@ GEM PLATFORMS aarch64-linux + x86_64-darwin-20 x86_64-linux DEPENDENCIES @@ -214,9 +230,12 @@ DEPENDENCIES puma (~> 5.0) rack-cors rails (~> 7.0.0) + rspec rspec-rails + rspec_junit_formatter rubocop shoulda-matchers (~> 5.0) + simplecov sprockets-rails stimulus-rails turbo-rails diff --git a/config/database.yml b/config/database.yml index 95970000f..9300ccacb 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,88 +1,19 @@ -# PostgreSQL. Versions 9.3 and up are supported. -# -# Install the pg driver: -# gem install pg -# On macOS with Homebrew: -# gem install pg -- --with-pg-config=/usr/local/bin/pg_config -# On macOS 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 - # https://guides.rubyonrails.org/configuring.html#database-pooling - pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + host: <%= ENV.fetch('POSTGRES_HOST', 'db') %> + username: <%= ENV.fetch('POSTGRES_USER', 'no_pg_user_set') %> + password: <%= ENV.fetch('POSTGRES_PASSWORD', '') %> + pool: <%= ENV.fetch('RAILS_MAX_THREADS', 5) %> development: <<: *default - host: db - username: postgres - password: password - database: choco_cake_development + database: <%= ENV.fetch('POSTGRES_DB', 'choco_cake_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 running Rails. - #username: app - - # 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: choco_cake_test + database: <%= ENV.fetch('POSTGRES_DB', 'choco_cake_test') %> -# As with config/credentials.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 or a full connection URL as an environment -# variable when you boot the app. For example: -# -# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" -# -# If the connection URL is provided in the special DATABASE_URL environment -# variable, Rails will automatically merge its configuration values on top of -# the values provided in this file. Alternatively, you can specify a connection -# URL environment variable explicitly: -# -# production: -# url: <%= ENV["MY_APP_DATABASE_URL"] %> -# -# Read https://guides.rubyonrails.org/configuring.html#configuring-a-database -# for a full overview on how database connection configuration can be specified. -# production: <<: *default url: <%= ENV['DATABASE_URL'] %> - diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 72785e11e..07f11b8f2 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -8,15 +8,20 @@ end describe 'identifier not nil' do - subject { FactoryBot.build(:project) } - it { is_expected.not_to allow_value(nil).for(:identifier) } + it 'generates an identifier if not present' do + proj = FactoryBot.build(:project, identifier: nil) + expect { proj.valid? } + .to change { proj.identifier.nil? } + .from(true) + .to(false) + end end describe 'identifier unique' do it do project1 = FactoryBot.create(:project) project2 = FactoryBot.build(:project, identifier: project1.identifier) - expect { project2.valid? }.to change { project2.identifier } + expect { project2.valid? }.to change(project2, :identifier) end end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index b4c930509..54505930c 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -1,4 +1,9 @@ # This file is copied to spec/ when you run 'rails generate rspec:install' +require 'simplecov' +SimpleCov.start 'rails' do + enable_coverage :branch +end + require 'spec_helper' ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../config/environment', __dir__)