diff --git a/test/test_cache_store.rb b/test/test_cache_store.rb new file mode 100644 index 0000000..11884a3 --- /dev/null +++ b/test/test_cache_store.rb @@ -0,0 +1,116 @@ +require 'minitest/autorun' +require 'prism' + +class CacheStoreTest < Minitest::Test + def setup + @store = Prism::Cache::MemoryStore.new + end + + def test_write_then_read_round_trips + @store.write('key', 'value') + assert_equal 'value', @store.read('key') + end + + def test_read_of_missing_key_is_nil + assert_nil @store.read('missing') + end + + def test_keys_are_coerced_to_string + @store.write(:sym_key, 'value') + assert_equal 'value', @store.read('sym_key') + end + + def test_fetch_computes_and_stores_on_miss + calls = 0 + 3.times { @store.fetch('k') { calls += 1; 'computed' } } + assert_equal 1, calls + end + + def test_fetch_returns_existing_value_without_yielding_on_hit + @store.write('k', 'preset') + result = @store.fetch('k') { raise 'must not be called' } + assert_equal 'preset', result + end + + def test_delete_removes_the_entry + @store.write('k', 'v') + assert @store.delete('k') + assert_nil @store.read('k') + end + + def test_delete_of_missing_key_returns_falsey + refute @store.delete('missing') + end + + def test_exist_reflects_presence + refute @store.exist?('k') + @store.write('k', 'v') + assert @store.exist?('k') + end + + def test_clear_empties_the_store + @store.write('a', 1) + @store.write('b', 2) + @store.clear + assert_equal 0, @store.size + end + + def test_expires_in_makes_the_entry_unreadable_afterwards + @store.write('k', 'v', expires_in: 0) + sleep 0.01 + assert_nil @store.read('k') + end + + def test_delete_matched_removes_keys_matching_a_regexp + @store.write('posts/1', 'a') + @store.write('posts/2', 'b') + @store.write('users/1', 'c') + @store.delete_matched(/\Aposts\//) + assert_nil @store.read('posts/1') + assert_nil @store.read('posts/2') + assert_equal 'c', @store.read('users/1') + end + + def test_default_store_is_a_memory_store_singleton + assert_same Prism::Cache.store, Prism::Cache.store + assert_instance_of Prism::Cache::MemoryStore, Prism::Cache.store + end + + def test_unbounded_store_never_evicts + store = Prism::Cache::MemoryStore.new + 50.times { |i| store.write("k#{i}", i) } + assert_equal 50, store.size + end + + def test_max_size_evicts_the_least_recently_used_entry_on_write + store = Prism::Cache::MemoryStore.new(max_size: 2) + store.write('a', 1) + store.write('b', 2) + store.write('c', 3) + assert_equal 2, store.size + assert_nil store.read('a') + assert_equal 2, store.read('b') + assert_equal 3, store.read('c') + end + + def test_reading_a_key_protects_it_from_eviction + store = Prism::Cache::MemoryStore.new(max_size: 2) + store.write('a', 1) + store.write('b', 2) + store.read('a') # bump 'a' to most-recently-used, 'b' is now the LRU one + store.write('c', 3) + assert_equal 1, store.read('a') + assert_nil store.read('b') + assert_equal 3, store.read('c') + end + + def test_rewriting_an_existing_key_does_not_grow_the_store + store = Prism::Cache::MemoryStore.new(max_size: 2) + store.write('a', 1) + store.write('b', 2) + store.write('a', 'updated') + assert_equal 2, store.size + assert_equal 'updated', store.read('a') + assert_equal 2, store.read('b') + end +end diff --git a/test/test_cli_assets_precompile.rb b/test/test_cli_assets_precompile.rb new file mode 100644 index 0000000..2b2be2e --- /dev/null +++ b/test/test_cli_assets_precompile.rb @@ -0,0 +1,58 @@ +require 'minitest/autorun' +require 'prism' +require 'tmpdir' +require 'fileutils' +require 'stringio' + +class CliAssetsPrecompileTest < Minitest::Test + def setup + @dir = Dir.mktmpdir('prism-cli-assets') + @cli = Prism::Cli.new(@dir) + @source = File.join(@dir, 'app', 'assets') + @output = File.join(@dir, 'public', 'assets') + FileUtils.mkdir_p(File.join(@source, 'stylesheets')) + File.write(File.join(@source, 'stylesheets', 'application.css'), 'body { color: red; }') + end + + def teardown + FileUtils.remove_entry(@dir) + end + + def test_precompiles_without_booting_an_application + manifest = @cli.assets_precompile(['--source=app/assets', '--output=public/assets'], out: StringIO.new) + logical = manifest.to_h.keys + assert_equal ['stylesheets/application.css'], logical + fingerprinted = manifest.lookup('stylesheets/application.css') + assert_match(/\Astylesheets\/application-[0-9a-f]{8}\.css\z/, fingerprinted) + assert File.exist?(File.join(@output, fingerprinted)) + assert File.exist?(File.join(@output, 'manifest.json')) + end + + def test_prints_a_summary_to_the_given_output + out = StringIO.new + @cli.assets_precompile(['--source=app/assets', '--output=public/assets'], out: out) + assert_match(/compiled 1 file\(s\) into/, out.string) + end + + def test_recompiling_unchanged_content_reproduces_the_same_fingerprint_and_removes_nothing_stale + first = @cli.assets_precompile(['--source=app/assets', '--output=public/assets'], out: StringIO.new) + second = @cli.assets_precompile(['--source=app/assets', '--output=public/assets'], out: StringIO.new) + assert_equal first.to_h, second.to_h + end + + def test_raises_usage_error_without_required_options + assert_raises(Prism::Cli::UsageError) { @cli.assets_precompile([]) } + assert_raises(Prism::Cli::UsageError) { @cli.assets_precompile(['--source=app/assets']) } + end + + def test_run_dispatches_the_assets_colon_precompile_command + original_stdout = $stdout + captured = StringIO.new + $stdout = captured + @cli.run(['assets:precompile', '--source=app/assets', '--output=public/assets']) + ensure + $stdout = original_stdout + assert_match(/compiled 1 file\(s\)/, captured.string) + assert File.exist?(File.join(@output, 'manifest.json')) + end +end