ardekantur-shnork 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ require 'rake'
2
+ require 'rake/gempackagetask'
3
+ require 'rubygems/specification'
4
+ require 'spec/rake/spectask'
5
+
6
+ task :default => :spec
7
+
8
+ desc "Run all specs"
9
+ Spec::Rake::SpecTask.new(:spec) do |t|
10
+ t.spec_files = FileList['spec/*.rb'] - ['spec/site.rb']
11
+ # t.spec_opts = ['--color']
12
+ end
13
+
14
+ spec = Gem::Specification.load('shnork.gemspec')
15
+
16
+ Rake::GemPackageTask.new(spec) do |pkg|
17
+ pkg.gem_spec = spec
18
+ end
19
+
20
+ desc "run rspec + rcov"
21
+ Spec::Rake::SpecTask.new("spec:rcov") do |t|
22
+ t.spec_files = FileList['spec/**/*_spec.rb']
23
+ t.rcov_opts = ['--exclude', "spec/,rcov.rb,rspec.rb,spec*,gems*"]
24
+ t.rcov = true
25
+ t.rcov_dir = 'doc/coverage'
26
+ end
27
+
28
+ desc "install the gem locally"
29
+ task :install => [:package] do
30
+ sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
31
+ end
@@ -0,0 +1,59 @@
1
+ module RspecShnork
2
+
3
+ def shnork thresholds = {404 => 0, 500 => 0}
4
+ RspecShnork.new(thresholds, '/')
5
+ end
6
+
7
+ def shnork_at path, thresholds = {404 => 0, 500 => 0}
8
+ RspecSnork.new(thresholds, path)
9
+ end
10
+
11
+ class RspecShnork
12
+
13
+ def initialize thresholds, path = nil
14
+ @path = path || '/'
15
+ @thresholds = thresholds
16
+ end
17
+
18
+ def matches?(target)
19
+ @results = Shnork::Hunt(@path)
20
+ messages_for(404) <= @thresholds[404] and messages_for(500) <= @thresholds[500]
21
+ end
22
+
23
+ def failure_message
24
+ max_messages = 5
25
+ msg = <<-EOM
26
+ Shnork found #{messages_to_string(404)} and #{messages_to_string(500)}.
27
+ One or both of these are over the accepted thresholds (404 -> #{@thresholds[404]}, 500 -> #{@thresholds[500]}).
28
+ EOM
29
+ [404, 500].each do |error|
30
+ i = 0
31
+ if messages_for(error) > 0 then
32
+ show_errors = messages_for(error) < max_messages ? messages_for(error) : max_messages
33
+ msg += " First #{show_errors} discovered #{error} error#{ show_errors == 1 ? '' : 's'}:\n"
34
+ while i < max_messages and i < messages_for(error)
35
+ msg += " * " + @results.codes[error][i] + "\n"
36
+ i += 1
37
+ end
38
+ end
39
+ end
40
+ return msg
41
+ end
42
+
43
+ def messages_for(error)
44
+ return 0 unless @results.codes.has_key? error
45
+ return @results.codes[error].size
46
+ end
47
+
48
+ def messages_to_string(error)
49
+ err = (messages_for(error) == 1) ? "error" : "errors"
50
+ "#{messages_for(error)} #{error} #{err}"
51
+ end
52
+
53
+ def negative_failure_message
54
+ "expected shnork not to pass"
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,104 @@
1
+ require 'nokogiri'
2
+ require 'rack'
3
+
4
+ $LOAD_PATH.unshift( File.dirname(__FILE__) )
5
+ require 'rspec_shnork_matchers'
6
+
7
+ module Shnork
8
+
9
+ def self.Hunt page
10
+ return Shnork.new().analyze(page)
11
+ end
12
+
13
+ def self.new
14
+ return Shnork.new()
15
+ end
16
+
17
+ class Shnork
18
+
19
+ include Rack::Utils
20
+
21
+ attr_reader :page, :reached
22
+ attr_accessor :depth
23
+
24
+ def initialize
25
+ set :env, :production
26
+ set :run, false
27
+ @app = Sinatra::application
28
+ @request = Rack::MockRequest.new(Sinatra.build_application)
29
+ @reached = []
30
+ @found = []
31
+
32
+ @internal_depth = {}
33
+ @internal_pred = {}
34
+ @depth = :all
35
+
36
+ @results = ShnorkResults.new
37
+ end
38
+
39
+ def analyze page
40
+ @found << [page, nil]
41
+ while @found.size != 0
42
+ link = @found.shift
43
+ next if @reached.include? link.first
44
+ retrieve link
45
+ end
46
+
47
+ return @results
48
+ end
49
+
50
+ def record page
51
+ @reached = @reached | [page]
52
+ end
53
+
54
+ def less_than_max_depth d
55
+ return true if @depth == :all
56
+ d <= @depth
57
+ end
58
+
59
+ def retrieve page_from
60
+ page = page_from.first
61
+ from = page_from.last
62
+ @internal_depth[page] = from ? @internal_depth[from] + 1 : 0
63
+ @internal_pred[page] = from
64
+ return unless less_than_max_depth @internal_depth[page]
65
+ @page = @request.get(page)
66
+ @page = @request.get(@page.headers["Location"]) if @page.status >= 300 and @page.status < 400
67
+ @results.add @page.status, page
68
+ @found = @found | links(page)
69
+ record page
70
+ end
71
+
72
+ def links current_page
73
+ return [] unless @page
74
+ Nokogiri::HTML(@page.body).xpath('//a').select { |link| link.attributes["href"] =~ /^\// }.map { |link| [link.attributes["href"], current_page] }
75
+ end
76
+
77
+ private
78
+
79
+ def status
80
+ puts "DEPTH: #{@internal_depth.inspect}"
81
+ puts "PREDS: #{@internal_pred.inspect}"
82
+ puts "FOUND: #{@found.inspect}"
83
+ puts "REACH: #{@reached.inspect}"
84
+ puts "----------------------"
85
+ end
86
+
87
+ end
88
+
89
+ class ShnorkResults
90
+
91
+ attr_reader :codes
92
+
93
+ def initialize
94
+ @codes = {}
95
+ end
96
+
97
+ def add return_code, page
98
+ @codes[return_code] ||= []
99
+ @codes[return_code] << page
100
+ end
101
+
102
+ end
103
+
104
+ end
@@ -0,0 +1,64 @@
1
+ # shnork
2
+
3
+ shnork is [dwemthy's][] lil' cousin. he's kinda headstrong but he doesn't take any guff. he explores your website like a dungeon, making sure it's structurally sound, all cavernous.
4
+
5
+ shnork is link testing for sinatra. lookatitgo!
6
+
7
+ ## features
8
+
9
+ * specify the depth you want to allow shnork to descend
10
+ * provide thresholds: allow _x_ number of `404`s or _y_ number of `500`s
11
+
12
+ ## syntax
13
+
14
+ describe "My awesome Sinatra web application" do
15
+ include RspecShnork
16
+
17
+ it "should be a perfect website" do
18
+ Sinatra::application.should shnork
19
+ end
20
+
21
+ it "should not be broken beyond four 404 errors and 9 500 errors" do
22
+ Sinatra::application.should shnork({404 => 4, 500 => 9})
23
+ end
24
+
25
+ it "should be a perfect from the members section in" do
26
+ Sinatra::application.should shnork_at '/members'
27
+ end
28
+
29
+ end
30
+
31
+ ## the results are in
32
+
33
+ Actual code results! **Exciting**.
34
+
35
+ 1)
36
+ 'Shnork RSpec matchers should work' FAILED
37
+ Shnork found 6 404 errors and 6 500 errors.
38
+ One or both of these are over the accepted thresholds (404 -> 0, 500 -> 0).
39
+ First 5 discovered 404 errors:
40
+ * /broken-link/1
41
+ * /broken-link/2
42
+ * /broken-link/3
43
+ * /broken-link/4
44
+ * /broken-link/5
45
+ First 2 discovered 500 errors:
46
+ * /error/1
47
+ * /error/2
48
+ * /error/3
49
+ ./spec/shnork_rspec_spec.rb:8:
50
+
51
+ Wootabega.
52
+
53
+ ## helping out
54
+
55
+ this code is in its infancy. please feel free to contribute suggestions as to what features you'd like. features have a greater chance of being implemented if you write a spec, or if you fork and write how you think it should work!
56
+
57
+ here's [where you can file bugs][].
58
+
59
+ ### misc
60
+
61
+ licensed under the gpl. enjoy.
62
+
63
+ [dwemthy's]: http://poignantguide.net/dwemthy/
64
+ [where you can file bugs]: http://ardekantur.lighthouseapp.com/projects/20004-shnork/overview
@@ -0,0 +1,52 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Shnork" do
4
+
5
+ setup do
6
+ Sinatra.application = nil
7
+
8
+ page = Proc.new { [
9
+ "<h1>My Web Page</h1>",
10
+ "This is my unsightly webpage! It's not even valid markup!",
11
+ "<a href='http://www.google.com'>Google</a>",
12
+ "<hr />",
13
+ '<a href="/login">Login</a>',
14
+ "<a href='/about'>About this page</a>"
15
+ ].join }
16
+ get '/', &page
17
+
18
+ about = Proc.new { "<a href='/second/layer/down'>Second layer down link</a>" }
19
+ get '/about', &about
20
+
21
+ second_layer_down = Proc.new { "You made it here!" }
22
+ get '/second/layer/down', &second_layer_down
23
+
24
+ @shallow_hunt = Shnork.new()
25
+ @shallow_hunt.depth = 0
26
+ @shallow_results = @shallow_hunt.analyze('/')
27
+
28
+ @deep_hunt = Shnork.new()
29
+ @deep_results = @deep_hunt.analyze('/')
30
+ end
31
+
32
+ teardown do
33
+ Sinatra.application = nil
34
+ end
35
+
36
+ it "should record the initial link as reached" do
37
+ @shallow_hunt.reached.should == ['/']
38
+ end
39
+
40
+ it "should provide information about the initial link in results" do
41
+ @shallow_results.codes.size.should == 1
42
+ @shallow_results.codes.should == { 200 => ['/'] }
43
+ end
44
+
45
+ it "should find links the second level down" do
46
+ @deep_results.codes[200].should include('/second/layer/down')
47
+ end
48
+
49
+ it "should appropriately handle redirects"
50
+ it "should should only descend to the desired depth"
51
+
52
+ end
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+
4
+ get '/' do
5
+ <<-EOM
6
+ Hello World!
7
+ <a href="/">Home</a>
8
+ <a href="/working-link">Working Link</a>
9
+ <a href="/working-link/1">Working Link</a>
10
+ <a href="/working-link/2">Working Link</a>
11
+ <a href="/working-link/3">Working Link</a>
12
+ <a href="/working-link/4">Working Link</a>
13
+ <a href="/working-link/5">Working Link</a>
14
+ <a href="/working-link/6">Working Link</a>
15
+ EOM
16
+ end
17
+
18
+ get '/working-link' do
19
+ <<-EOM
20
+ This is a working page!
21
+ EOM
22
+ end
23
+
24
+ get '/working-link/:number' do
25
+ <<-EOM
26
+ This is a working page!
27
+ Here is another: <a href='/working-link'>Working Link</a>
28
+ This is a broken link: <a href='/broken-link/#{params[:number]}'>Broken Link #{params[:number]}</a>
29
+ This link will cause a 500 error: <a href='/error/#{params[:number]}'>500 Error link #{params[:number]}</a>
30
+ EOM
31
+ end
32
+
33
+ error do
34
+ "<h1>500</h1>"
35
+ end
36
+
37
+ get '/error/:number' do
38
+ throw :halt, [500, 'error']
39
+ end
40
+
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+ require 'spec/interop/test'
4
+ require 'sinatra/test/unit'
5
+
6
+ require File.dirname(__FILE__) + '/../lib/shnork'
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ardekantur-shnork
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - ardekantur
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-12-14 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: nokogiri
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.0.2
23
+ version:
24
+ description: make sure your sinatra websites are logically sound
25
+ email: greystone@ardekantur.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - readme.md
34
+ - Rakefile
35
+ - lib/shnork.rb
36
+ - lib/rspec_shnork_matchers.rb
37
+ - spec/shnork_spec.rb
38
+ - spec/spec_helper.rb
39
+ - spec/site.rb
40
+ has_rdoc: false
41
+ homepage: http://github.com/ardekantur/shnork
42
+ post_install_message:
43
+ rdoc_options: []
44
+
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ requirements: []
60
+
61
+ rubyforge_project:
62
+ rubygems_version: 1.2.0
63
+ signing_key:
64
+ specification_version: 2
65
+ summary: make sure your sinatra websites are logically sound
66
+ test_files:
67
+ - spec/shnork_spec.rb