Skip to the content.

Home

Previous: Chapter 12 - The Archive


📝 As an app user, I want archived users not to be selected, so that we always have a valid host.

Now that we have the archived column working we need to put it to use.

Add some archived users to the fixtures to help with testing.

test/fixtures/users.yml

sarah:
  name: Sarah Fleming
  email: sarah@example.com

chantel:
  name: Chantel Miller
  email: chantel@example.com

adrian:
  name: Adrian Lee
  email: adrian@example.com

daniel:
  name: Daniel Bryant
  email: daniel@example.com

casper:
  name: Casper
  email: casper@example.com
  archived: true

stretch:
  name: Stretch
  email: stretch@example.com
  archived: true

fatso:
  name: Fatso
  email: fatso@example.com
  archived: true

stinkie:
  name: Stinkie
  email: stinkie@example.com
  archived: true

We know that we’re dealing with selections, so a good place to start is our Selection model. After quickly reminding ourselves of the logic in after_initialize, our eyes zero in on round.available_users.sample. We’ll need to refactor Round#available_users so that it does not return archived users. Continuing this process, we’ll push down into our models until we’ve made adequate changes. Then we’ll pop back up, testing each layer. We won’t need any changes to Selection, but we should still add to the test.

Refactor Round#available_users to not return archived users.

app/models/round.rb

class Round < ApplicationRecord
  has_many :selections, -> { order(:id) }
  has_many :users, through: :selections

  def available_users
    User.unarchived.without(users)
  end
end

Add the missing User.unarchived method in the form of a scope.

app/models/user.rb

class User < ApplicationRecord
  validates :name, :email, presence: true
  validates :email, uniqueness: { case_sensitive: false }
  validates :archived, exclusion: [nil]

  scope :unarchived, -> { where(archived: false) }
end

Add tests for User.unarchived.

test/models/user_test.rb

require "test_helper"

class UserTest < ActiveSupport::TestCase
  context "validations" do
    should validate_presence_of(:name)
    should validate_presence_of(:email)
    should validate_uniqueness_of(:email).case_insensitive
    should validate_exclusion_of(:archived).in_array([nil])
  end

  test ".unarchived" do
    unarchived_users = [
      users(:sarah),
      users(:chantel),
      users(:adrian),
      users(:daniel),
    ]

    assert_equal unarchived_users.sort, User.unarchived.sort
  end
end

Run the tests.

Terminal

bin/rails t test/models/user_test.rb

Add tests for Round#available_users.

test/models/round_test.rb

require "test_helper"

class RoundTest < ActiveSupport::TestCase
  context "associations" do
    should have_many(:selections).order(:id)
    should have_many(:users).through(:selections)
  end

  context "#available_users" do
    should "return all available users when none have been selected" do
      available_users = [
        users(:sarah),
        users(:chantel),
        users(:adrian),
        users(:daniel),
      ]

      round = rounds(:empty)
      assert_equal available_users.sort, round.available_users.sort
    end

    should "return no users when all available have been selected" do
      round = rounds(:full)
      assert_equal [], round.available_users
    end
  end
end

Run the tests.

Terminal

Running 4 tests in a single process (parallelization threshold is 50)
Run options: --seed 39956

# Running:

....

Finished in 0.072143s, 55.4454 runs/s, 55.4454 assertions/s.
4 runs, 4 assertions, 0 failures, 0 errors, 0 skips

Finish up with the selection tests.

test/models/selection_test.rb

require "test_helper"

class SelectionTest < ActiveSupport::TestCase
  context "associations" do
    should belong_to(:round)
    should belong_to(:user)
  end

  context "validations" do
    should validate_uniqueness_of(:user_id).scoped_to(:round_id)
  end

  context "after initialize" do
    should "select a random available user" do
      available_users = [
        users(:sarah),
        users(:chantel),
        users(:adrian),
        users(:daniel),
      ]

      unavailable_users = [
        users(:casper),
        users(:stretch),
        users(:fatso),
        users(:stinkie),
      ]

      round = rounds(:empty)
      selection = Selection.new(round: round)
      assert_includes available_users, selection.user
      assert_not_includes unavailable_users, selection.user
    end

    should "not select a user when round is not set" do
      selection = Selection.new
      assert_nil selection.user
    end

    should "not select a user when one has been set" do
      round = rounds(:empty)
      user = users(:daniel)
      selection = Selection.new(round: round, user: user)
      assert_equal user, selection.user
    end

    should "not select a user when all have been selected" do
      round = rounds(:full)
      selection = Selection.new(round: round)
      assert_nil selection.user
    end
  end
end

Run the tests.

Terminal

bin/rails t test/models/selection_test.rb

Check for regressions by running all tests.

Terminal

bin/rails t

Uh oh! Our users query is returning more data than our test is expecting because of the new fixtures. Easy fix.

Update the test.

test/integration/types/query_type/users_test.rb

require "test_helper"

class Types::QueryType::UsersTest < ActionDispatch::IntegrationTest
  test "users" do
    query = <<~GRAPHQL
      {
        users {
          id
          name
          email
          createdAt
          updatedAt
          archived
        }
      }
    GRAPHQL

    post graphql_path, params: { query: query }

    users_in_email_order = [
      users(:adrian),
      users(:casper),
      users(:chantel),
      users(:daniel),
      users(:fatso),
      users(:sarah),
      users(:stinkie),
      users(:stretch),
    ]

    assert_equal(
      {
        "data" => {
          "users" => users_in_email_order.map { |user|
            {
              "id" => user.id.to_s,
              "name" => user.name,
              "email" => user.email,
              "createdAt" => user.created_at.iso8601,
              "updatedAt" => user.updated_at.iso8601,
              "archived" => user.archived,
            }
          },
        },
      },
      @response.parsed_body
    )
  end
end

Check for regressions by running all tests (again).

Terminal

bin/rails t

Success!

âś… Make a commit

âś… As an app user, I want archived users not to be selected, so that we always have a valid host.


Next: Chapter 14 - The Goodbye