# frozen_string_literal: true

require "spec_helper"

describe "SecretStorable" do
  let(:clazz) do
    Class.new do
      include Doorkeeper::Models::SecretStorable

      def self.find_by(*)
        raise "stub this"
      end

      def update_column(*)
        raise "stub this"
      end

      def token
        raise "stub this"
      end
    end
  end
  let(:strategy) { clazz.secret_strategy }

  describe :find_by_plaintext_token do
    subject { clazz.send(:find_by_plaintext_token, "attr", "input") }

    it "forwards to the secret_strategy" do
      expect(strategy)
        .to receive(:transform_secret)
        .with("input")
        .and_return "found"

      expect(clazz)
        .to receive(:find_by)
        .with("attr" => "found")
        .and_return "result"

      expect(subject).to eq "result"
    end

    it "calls find_by_fallback_token if not found" do
      expect(clazz)
        .to receive(:find_by)
        .with("attr" => "input")
        .and_return nil

      expect(clazz)
        .to receive(:find_by_fallback_token)
        .with("attr", "input")
        .and_return "fallback"

      expect(subject).to eq "fallback"
    end
  end

  describe :find_by_fallback_token do
    subject { clazz.send(:find_by_fallback_token, "attr", "input") }
    let(:fallback) { double(::Doorkeeper::SecretStoring::Plain) }

    it "returns nil if none defined" do
      expect(clazz.fallback_secret_strategy).to eq nil
      expect(subject).to eq nil
    end

    context "if a fallback strategy is defined" do
      before do
        allow(clazz).to receive(:fallback_secret_strategy).and_return(fallback)
      end

      context "if a resource is defined" do
        let(:resource) { double("Token model") }

        it "calls the strategy for lookup" do
          expect(clazz)
            .to receive(:find_by)
            .with("attr" => "fallback")
            .and_return(resource)

          expect(fallback)
            .to receive(:transform_secret)
            .with("input")
            .and_return("fallback")

          # store_secret will call the resource
          expect(resource)
            .to receive(:attr=)
            .with("new value")

          # It will upgrade the secret automtically using the current strategy
          expect(strategy)
            .to receive(:transform_secret)
            .with("input")
            .and_return("new value")

          expect(resource).to receive(:update).with("attr" => "new value")
          expect(subject).to eq resource
        end
      end

      context "if a resource is not defined" do
        before do
          allow(clazz).to receive(:fallback_secret_strategy).and_return(fallback)
        end

        it "returns nil" do
          expect(clazz)
            .to receive(:find_by)
            .with("attr" => "fallback")
            .and_return(nil)

          expect(fallback)
            .to receive(:transform_secret)
            .with("input")
            .and_return("fallback")

          # It does not find a token even with the fallback method
          expect(subject).to be_nil
        end
      end
    end
  end

  describe :secret_strategy do
    it "defaults to plain strategy" do
      expect(strategy).to eq Doorkeeper::SecretStoring::Plain
    end
  end

  describe :fallback_secret_strategy do
    it "defaults to nil" do
      expect(clazz.fallback_secret_strategy).to eq nil
    end
  end
end
