2857: fix flaky time-sensitive tests
authorTim Pierce <twp@curoverse.com>
Thu, 5 Jun 2014 19:42:06 +0000 (15:42 -0400)
committerTim Pierce <twp@curoverse.com>
Thu, 5 Jun 2014 19:44:15 +0000 (15:44 -0400)
Allow tests to inject a mock Time object into Blob, in order to generate
consistent behavior for tests that rely on permission signatures.

Closes #2857.

services/api/app/models/blob.rb
services/api/test/functional/arvados/v1/collections_controller_test.rb

index 5decd77261a44bdc0ec4145cf5b3fce80aa7cb2b..c4e1881b29a6093cd12fefd2ffc49c469879dd1a 100644 (file)
@@ -16,6 +16,15 @@ class Blob
   class InvalidSignatureError < StandardError
   end
 
+  # Clock is a wrapper for Time.
+  # It can be replaced with a stub for unit testing (the poor man's mock).
+  # It must support a Clock.now method that returns a Time object.
+  @@Clock = Time
+
+  def self.set_clock c
+    @@Clock = c
+  end
+
   # Blob.sign_locator: return a signed and timestamped blob locator.
   #
   # The 'opts' argument should include:
@@ -35,7 +44,7 @@ class Blob
       end
       timestamp = opts[:expire]
     else
-      timestamp = Time.now.to_i + (opts[:ttl] || 600)
+      timestamp = @@Clock.now.to_i + (opts[:ttl] || 600)
     end
     timestamp_hex = timestamp.to_s(16)
     # => "53163cb4"
@@ -82,7 +91,7 @@ class Blob
     if !timestamp.match /^[\da-f]+$/
       raise Blob::InvalidSignatureError.new 'Timestamp is not a base16 number.'
     end
-    if timestamp.to_i(16) < Time.now.to_i
+    if timestamp.to_i(16) < @@Clock.now.to_i
       raise Blob::InvalidSignatureError.new 'Signature expiry time has passed.'
     end
 
index 8b2725a4bd266cf420fc71fc2a2f2bb356f9b860..ba6929be6497dfc3e62dd9245143d3b5a602e9e5 100644 (file)
@@ -2,6 +2,25 @@ require 'test_helper'
 
 class Arvados::V1::CollectionsControllerTest < ActionController::TestCase
 
+  # StoppedClock.now always returns the same timestamp.
+  # Set the Blob permission signing clock to ensure that
+  # all permission hints use consistent timestamps for testing.
+
+  class StoppedClock
+    @@cached_timestamp = Time.now
+    def self.now
+      return @@cached_timestamp
+    end
+  end
+
+  def setup
+    Blob.set_clock(StoppedClock)
+  end
+
+  def teardown
+    Blob.set_clock(Time)
+  end
+
   test "should get index" do
     authorize_with :active
     get :index