adhearsion 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. data/.gitignore +10 -0
  2. data/CHANGELOG +8 -0
  3. data/README.markdown +33 -0
  4. data/Rakefile +28 -68
  5. data/adhearsion.gemspec +19 -133
  6. data/app_generators/ahn/templates/Gemfile +0 -4
  7. data/app_generators/ahn/templates/components/disabled/restful_rpc/spec/restful_rpc_spec.rb +4 -16
  8. data/lib/adhearsion/cli.rb +17 -0
  9. data/lib/adhearsion/component_manager/component_tester.rb +1 -3
  10. data/lib/adhearsion/component_manager/spec_framework.rb +4 -10
  11. data/lib/adhearsion/foundation/object.rb +10 -0
  12. data/lib/adhearsion/version.rb +1 -1
  13. data/spec/ahn_command_spec.rb +284 -0
  14. data/spec/component_manager_spec.rb +292 -0
  15. data/spec/constants_spec.rb +8 -0
  16. data/spec/drb_spec.rb +65 -0
  17. data/spec/fixtures/dialplan.rb +3 -0
  18. data/spec/foundation/event_socket_spec.rb +168 -0
  19. data/spec/host_definitions_spec.rb +79 -0
  20. data/spec/initialization_spec.rb +163 -0
  21. data/spec/initializer/configuration_spec.rb +270 -0
  22. data/spec/initializer/loading_spec.rb +149 -0
  23. data/spec/initializer/paths_spec.rb +74 -0
  24. data/spec/logging_spec.rb +86 -0
  25. data/spec/relationship_properties_spec.rb +54 -0
  26. data/spec/silence.rb +10 -0
  27. data/spec/spec_helper.rb +101 -0
  28. data/spec/voip/asterisk/agi_server_spec.rb +473 -0
  29. data/spec/voip/asterisk/ami/ami_spec.rb +549 -0
  30. data/spec/voip/asterisk/ami/lexer/ami_fixtures.yml +30 -0
  31. data/spec/voip/asterisk/ami/lexer/lexer_story +291 -0
  32. data/spec/voip/asterisk/ami/lexer/lexer_story.rb +241 -0
  33. data/spec/voip/asterisk/ami/lexer/story_helper.rb +124 -0
  34. data/spec/voip/asterisk/ami/old_tests.rb +204 -0
  35. data/spec/voip/asterisk/ami/super_manager/super_manager_story +25 -0
  36. data/spec/voip/asterisk/ami/super_manager/super_manager_story.rb +15 -0
  37. data/spec/voip/asterisk/ami/super_manager/super_manager_story_helper.rb +5 -0
  38. data/spec/voip/asterisk/commands_spec.rb +2179 -0
  39. data/spec/voip/asterisk/config_file_generators/agents_spec.rb +251 -0
  40. data/spec/voip/asterisk/config_file_generators/queues_spec.rb +323 -0
  41. data/spec/voip/asterisk/config_file_generators/voicemail_spec.rb +306 -0
  42. data/spec/voip/asterisk/config_manager_spec.rb +127 -0
  43. data/spec/voip/asterisk/menu_command/calculated_match_spec.rb +109 -0
  44. data/spec/voip/asterisk/menu_command/matchers_spec.rb +97 -0
  45. data/spec/voip/call_routing_spec.rb +125 -0
  46. data/spec/voip/dialplan_manager_spec.rb +468 -0
  47. data/spec/voip/dsl/dialing_dsl_spec.rb +270 -0
  48. data/spec/voip/dsl/dispatcher_spec.rb +82 -0
  49. data/spec/voip/dsl/dispatcher_spec_helper.rb +45 -0
  50. data/spec/voip/dsl/parser_spec.rb +69 -0
  51. data/spec/voip/freeswitch/basic_connection_manager_spec.rb +39 -0
  52. data/spec/voip/freeswitch/inbound_connection_manager_spec.rb +39 -0
  53. data/spec/voip/freeswitch/oes_server_spec.rb +9 -0
  54. data/spec/voip/numerical_string_spec.rb +61 -0
  55. data/spec/voip/phone_number_spec.rb +45 -0
  56. data/theatre-spec/dsl_examples/dynamic_stomp.rb +7 -0
  57. data/theatre-spec/dsl_examples/simple_before_call.rb +7 -0
  58. data/theatre-spec/dsl_spec.rb +43 -0
  59. data/theatre-spec/invocation_spec.rb +167 -0
  60. data/theatre-spec/namespace_spec.rb +125 -0
  61. data/theatre-spec/spec_helper.rb +37 -0
  62. data/theatre-spec/spec_helper_spec.rb +28 -0
  63. data/theatre-spec/theatre_class_spec.rb +150 -0
  64. metadata +171 -34
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require 'adhearsion/voip/freeswitch/basic_connection_manager'
3
+
4
+ include Adhearsion::VoIP::FreeSwitch
5
+
6
+ describe "FreeSwitch BasicConnectionManager" do
7
+ attr_reader :manager, :io
8
+ before(:each) do
9
+ @io = StringIO.new
10
+ @manager = BasicConnectionManager.new io
11
+ end
12
+
13
+ it "<<() should add two newlines" do
14
+ manager << "foobar"
15
+ io.string.should == "foobar\n\n"
16
+ end
17
+
18
+ end
19
+
20
+ describe "FreeSwitch BasicConnectionManager's header parser" do
21
+ it "YAML-like headers are read properly" do
22
+ header = {
23
+ "Foo-Bar" => "bar",
24
+ "Qaz-Monkey-Charlie-Zebra" => "qwerty"
25
+ }
26
+
27
+ string_header = header.inject("") do |string, (key, value)|
28
+ string + "#{key}: #{value}\n"
29
+ end
30
+
31
+ string_header << "\n"
32
+
33
+ manager = BasicConnectionManager.new StringIO.new(string_header)
34
+ manager.get_raw_header.should == string_header.strip
35
+
36
+ manager = BasicConnectionManager.new StringIO.new(string_header)
37
+ manager.get_header.should == header
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require 'adhearsion/voip/freeswitch/inbound_connection_manager'
3
+ include Adhearsion::VoIP::FreeSwitch
4
+
5
+ describe "A FreeSwitch InboundConnectionManager" do
6
+
7
+ it "authenticatating with the given password" do
8
+ manager = InboundConnectionManager.new io_mock
9
+ manager.login password
10
+ end
11
+
12
+ it "a hash is accepted when creating a new InboundConnectionManager" do
13
+ host, port = "myhost.mydomain", 31337
14
+
15
+ flexmock(TCPSocket).should_receive(:new).once.with(host, port).and_return io_mock
16
+
17
+ InboundConnectionManager.new :host => host, :port => port, :pass => password
18
+ end
19
+
20
+ it "an IO is accepted when creating a new InboundConnectionManager"
21
+
22
+ private
23
+ def io_mock
24
+ @io_mock ||=
25
+ begin
26
+ io_mock = StringIO.new
27
+ flexmock(io_mock) do |io|
28
+ io.should_receive(:write).with("auth #{password}\n\n")
29
+ io.should_receive(:gets).and_return "connection: kthnx\n",
30
+ "\n", "login: +OK\n", "\n"
31
+ end
32
+ io_mock
33
+ end
34
+ end
35
+
36
+ def password
37
+ "supersecret"
38
+ end
39
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+ require File.dirname(__FILE__) + "/../dsl/dispatcher_spec_helper"
3
+ require 'adhearsion/voip/freeswitch/oes_server'
4
+
5
+
6
+ describe "Adhearsion::VoIP::FreeSwitch::OesServer::OesDispatcher" do
7
+ include StandardDispatcherBehavior
8
+ before(:each) { @dispatcher_class = Adhearsion::VoIP::FreeSwitch::OesServer::OesDispatcher }
9
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ require 'adhearsion/voip/dsl/numerical_string'
4
+ require 'adhearsion/voip/constants'
5
+
6
+ describe "A NumericalString" do
7
+ # FIXME: This test is fundamentally broken in Ruby 1.9.
8
+ # See https://adhearsion.lighthouseapp.com/projects/5871/tickets/127-ruby-19-and-numericalstring-comparisons-in-case-statements
9
+ # The suggested workaround is to cast the object to a string:
10
+ # case numerical_string_object.to_s
11
+ # when "0987" then ...
12
+ # end
13
+ # it "should appear to be behave like a Fixnum in a case statement" do
14
+ # case numerical_string_for("123")
15
+ # when 123 then true
16
+ # else false
17
+ # end.should be true
18
+ #
19
+ # case numerical_string_for("0987")
20
+ # when 987 then true
21
+ # else false
22
+ # end.should be true
23
+ # end
24
+
25
+ it "should appear to behave like a String in a case statement" do
26
+ numerical_string_for("123").should === "123"
27
+ numerical_string_for("0987").should === "0987"
28
+ end
29
+
30
+ it "when compared against a Range that contains the numeric equivalent, the NumericalString is seen as a member" do
31
+ (100..200).should === numerical_string_for("150")
32
+ (100..200).should === numerical_string_for("0150")
33
+ (100..200).should_not === numerical_string_for("1000000")
34
+ end
35
+
36
+ it "comparing against a regular expression works" do
37
+ %r|^\d+$|.should === numerical_string_for("027316287")
38
+ end
39
+
40
+ it "checking if a string representation of a number starts with a leading zero" do
41
+ with_leading_zeros = %w(01 01234 01.23 01.2)
42
+ without_leading_zeros = %w(1 1.2 0 0.0)
43
+
44
+ with_leading_zeros.each do |number|
45
+ numerical_string.starts_with_leading_zero?(number).should_not be false
46
+ end
47
+
48
+ without_leading_zeros.each do |number|
49
+ numerical_string.starts_with_leading_zero?(number).should_not be true
50
+ end
51
+ end
52
+
53
+ private
54
+ def numerical_string
55
+ Adhearsion::VoIP::DSL::NumericalString
56
+ end
57
+
58
+ def numerical_string_for(string)
59
+ numerical_string.new(string)
60
+ end
61
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+ require 'adhearsion/voip/constants'
3
+ require 'adhearsion/voip/dsl/numerical_string'
4
+
5
+
6
+ # Use cases...
7
+ # case extension
8
+ # when US_NUMBER
9
+ # when (100..200)
10
+ # when _('12Z')
11
+ # when 123
12
+ # when "123"
13
+ # end
14
+
15
+ def should_be_nil_or_false(arg)
16
+ [nil, false].include?(arg).should == true
17
+ end
18
+
19
+ def should_not_be_nil_or_false(arg)
20
+ [nil, false].include?(arg).should == false
21
+ end
22
+
23
+ describe "A PhoneNumber" do
24
+
25
+ it "should have an ISN pattern-matching method" do
26
+ should_be_nil_or_false Adhearsion::VoIP::DSL::PhoneNumber.new("0115544332211").isn?
27
+ should_not_be_nil_or_false Adhearsion::VoIP::DSL::PhoneNumber.new("1*548").isn?
28
+ end
29
+
30
+ it "should have a US local number pattern-matching method" do
31
+ should_be_nil_or_false Adhearsion::VoIP::DSL::PhoneNumber.new("18887776665555").us_local_number?
32
+ should_be_nil_or_false Adhearsion::VoIP::DSL::PhoneNumber.new("18887776665555").us_national_number?
33
+
34
+ should_be_nil_or_false Adhearsion::VoIP::DSL::PhoneNumber.new("8887776665555").us_local_number?
35
+ should_be_nil_or_false Adhearsion::VoIP::DSL::PhoneNumber.new("8887776665555").us_national_number?
36
+
37
+ should_not_be_nil_or_false Adhearsion::VoIP::DSL::PhoneNumber.new("4445555").us_local_number?
38
+ should_be_nil_or_false Adhearsion::VoIP::DSL::PhoneNumber.new("4445555").us_national_number?
39
+ end
40
+
41
+ it "should convert from vanity numbers properly" do
42
+ Adhearsion::VoIP::DSL::PhoneNumber.from_vanity("1-800-FUDGEME").should == "18003834363"
43
+ end
44
+
45
+ end
@@ -0,0 +1,7 @@
1
+ events.stomp.this_is_a_dynamic_name.each do
2
+
3
+ end
4
+
5
+ events.stomp.new_call.each do
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ =begin YAML
2
+ namespaces:
3
+ - ["asterisk", "before_call"]
4
+ =end
5
+ events.asterisk.before_call.each do |call|
6
+ call.variables[:channel]
7
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe "CallbackDefinitionContainer" do
4
+
5
+ it "should successfully load the :simple_before_call example" do
6
+ example = Example.new(:simple_before_call)
7
+ theatre = Theatre::Theatre.new
8
+ example.register_namespaces_on theatre
9
+
10
+ flexmock(theatre.namespace_manager).should_receive(:register_callback_at_namespace).
11
+ with([:asterisk, :before_call], Proc).once
12
+
13
+ loader = Theatre::CallbackDefinitionLoader.new(theatre)
14
+ loader.load_events_file example.file
15
+ end
16
+
17
+ it "should let you override the recorder method name" do
18
+ theatre = Theatre::Theatre.new
19
+ theatre.namespace_manager.register_namespace_name "/foo/bar/qaz"
20
+ flexmock(theatre.namespace_manager).should_receive(:register_callback_at_namespace).
21
+ with([:foo, :bar, :qaz], Proc).once
22
+
23
+ loader = Theatre::CallbackDefinitionLoader.new(theatre, :roflcopter)
24
+ loader.roflcopter.foo.bar.qaz.each {}
25
+ end
26
+
27
+ end
28
+
29
+ # High level specs to test the entire library.
30
+
31
+ describe "Misuses of the Theatre" do
32
+
33
+ it "should not allow callbacks to be registered for namespaces which have not been registered" do
34
+ theatre = Theatre::Theatre.new
35
+ example = Example.new(:simple_before_call)
36
+
37
+ loader = Theatre::CallbackDefinitionLoader.new(theatre)
38
+ lambda do
39
+ loader.events.foo.each {}
40
+ end.should raise_error(Theatre::NamespaceNotFound)
41
+ end
42
+
43
+ end
@@ -0,0 +1,167 @@
1
+ require 'spec_helper'
2
+
3
+ GUID_REGEXP = /^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$/i
4
+
5
+ module InvocationTestHelper
6
+ def new_invocation(payload=@payload)
7
+ Theatre::Invocation.new(@namespace, @block, @payload)
8
+ end
9
+ end
10
+
11
+ describe "The lifecycle of an Invocation" do
12
+
13
+ include InvocationTestHelper
14
+
15
+ before :all do
16
+ @block = lambda {}
17
+ @payload = 123
18
+ @namespace = "/some/namespace"
19
+ end
20
+
21
+ it "should have an initial state of :new" do
22
+ new_invocation.current_state.should eql(:new)
23
+ end
24
+
25
+ it "should not have a @queued_time until state becomes :queued" do
26
+ invocation = new_invocation
27
+ invocation.queued_time.should eql(nil)
28
+ invocation.queued
29
+ invocation.queued_time.should be_instance_of(Time)
30
+ invocation.current_state.should eql(:queued)
31
+ end
32
+
33
+ it "should have a valid guid when instantiated" do
34
+ new_invocation.unique_id.should =~ GUID_REGEXP
35
+ end
36
+
37
+ it "should execute the callback when moving to the 'start' state" do
38
+ flexmock(@block).should_receive(:call).once
39
+ invocation = new_invocation
40
+ invocation.queued
41
+ invocation.start
42
+ end
43
+
44
+ end
45
+
46
+ describe "Using Invocations that've been ran through the Theatre" do
47
+
48
+ it "should pass the payload to the callback" do
49
+ destined_payload = [:i_feel_so_pretty, :OH, :SO, :PRETTY!]
50
+ expecting_callback = lambda do |payload|
51
+ payload.should equal(destined_payload)
52
+ end
53
+ invocation = Theatre::Invocation.new("/namespace/whatever", expecting_callback, destined_payload)
54
+ invocation.queued
55
+ invocation.start
56
+ end
57
+
58
+ it "should have a status of :error if an exception was raised and set the #error property" do
59
+ errorful_callback = lambda { raise ArgumentError, "this error is intentional" } # Simulate logic error
60
+ invocation = Theatre::Invocation.new("/namespace/whatever", errorful_callback)
61
+ invocation.queued
62
+ invocation.start
63
+ invocation.current_state.should equal(:error)
64
+ invocation.should be_error
65
+ invocation.error.should be_instance_of(ArgumentError)
66
+ end
67
+
68
+ it "should have a status of :success if no expection was raised" do
69
+ callback = lambda { "No errors raised here!" }
70
+ invocation = Theatre::Invocation.new("/namespace/whatever", callback)
71
+ invocation.queued
72
+ invocation.start
73
+ invocation.current_state.should equal(:success)
74
+ invocation.should be_success
75
+ end
76
+
77
+ it "should set the #returned_value property to the returned value callback when a payload was given" do
78
+ doubler = lambda { |num| num * 2 }
79
+ invocation = Theatre::Invocation.new('/foo/bar', doubler, 5)
80
+ invocation.queued
81
+ invocation.start
82
+ invocation.returned_value.should equal(10)
83
+ end
84
+
85
+ it "should set the #returned_value property to the returned value callback when a payload was NOT given" do
86
+ doubler = lambda { :ohai }
87
+ invocation = Theatre::Invocation.new('/foo/bar', doubler)
88
+ invocation.queued
89
+ invocation.start
90
+ invocation.returned_value.should equal(:ohai)
91
+ end
92
+
93
+ it "should set the #finished_time property when a success was encountered" do
94
+ block = lambda {}
95
+ invocation = Theatre::Invocation.new('/foo/bar', block)
96
+ invocation.queued
97
+
98
+ now = Time.now
99
+ flexmock(Time).should_receive(:now).twice.and_return now
100
+
101
+ invocation.start
102
+ invocation.should be_success
103
+ end
104
+
105
+ it "should set the #finished_time property when a failure was encountered" do
106
+ block = lambda { raise LocalJumpError }
107
+ invocation = Theatre::Invocation.new('/foo/bar', block)
108
+ invocation.queued
109
+
110
+ now = Time.now
111
+ flexmock(Time).should_receive(:now).twice.and_return now
112
+
113
+ invocation.start
114
+ invocation.should be_error
115
+ end
116
+
117
+ it "should set the #started_time property after starting" do
118
+ invocation = Theatre::Invocation.new('/foo/bar', lambda { sleep 0.01 } )
119
+ invocation.queued
120
+ invocation.started_time.should be_nil
121
+ invocation.start
122
+ invocation.started_time.should be_kind_of(Time)
123
+ end
124
+
125
+ it "should properly calculate #execution_duration" do
126
+ time_ago_difference = 60 * 5 # Five minutes
127
+ time_now = Time.now
128
+ time_ago = time_now - time_ago_difference
129
+
130
+ invocation = Theatre::Invocation.new('/foo/bar', lambda {} )
131
+ invocation.queued
132
+ invocation.start
133
+
134
+ invocation.send(:instance_variable_set, :@started_time, time_ago)
135
+ invocation.send(:instance_variable_set, :@finished_time, time_now)
136
+
137
+ invocation.execution_duration.should be_close(time_ago_difference.to_f, 0.01)
138
+ end
139
+
140
+ it "should return the set value of returned_value when one has been set to a non-nil value" do
141
+ return_nil = lambda { 123 }
142
+ invocation = Theatre::Invocation.new("/namespace/whatever", return_nil)
143
+ invocation.queued
144
+ invocation.start
145
+ invocation.returned_value.should eql(123)
146
+ end
147
+
148
+ it "should return nil for returned_value when it has been set to nil" do
149
+ return_nil = lambda { nil }
150
+ invocation = Theatre::Invocation.new("/namespace/whatever", return_nil)
151
+ invocation.queued
152
+ invocation.start
153
+ invocation.returned_value.should eql(nil)
154
+ end
155
+
156
+ it "waiting on an Invocation should execute properly" do
157
+ wait_on_invocation = lambda { 123 }
158
+ invocation = Theatre::Invocation.new("/namespace/whatever", wait_on_invocation)
159
+ Thread.new do
160
+ invocation.queued
161
+ invocation.start
162
+ end
163
+ invocation.wait.should eql(123)
164
+ invocation.success?.should eql(true)
165
+ end
166
+
167
+ end
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+
3
+ module NamespaceHelper
4
+ class BeValidNamespace
5
+
6
+ def matches?(target)
7
+ @target = target
8
+ Theatre::ActorNamespaceManager.valid_namespace_path? target
9
+ end
10
+
11
+ def failure_message
12
+ "expected #{@target.inspect} to be a valid namespace"
13
+ end
14
+
15
+ def negative_failure_message
16
+ "expected #{@target.inspect} not to be a valid namespace"
17
+ end
18
+
19
+ end
20
+
21
+ def be_valid_actor_event_namespace
22
+ BeValidNamespace.new
23
+ end
24
+
25
+ end
26
+
27
+ describe "ActorNamespaceManager" do
28
+
29
+ it "should make a registered namespace findable once registered" do
30
+ nm = Theatre::ActorNamespaceManager.new
31
+ nm.register_namespace_name "/foo/bar/qaz"
32
+ nm.search_for_namespace("/foo/bar/qaz").should be_kind_of(Theatre::ActorNamespaceManager::NamespaceNode)
33
+ end
34
+
35
+ it "should return the new namespace when registering it" do
36
+
37
+ end
38
+
39
+ it "#search_for_namespace should raise a NamespaceNotFound exception if a namespace one level deep was not found" do
40
+ nm = Theatre::ActorNamespaceManager.new
41
+ lambda do
42
+ nm.search_for_namespace "/foo"
43
+ end.should raise_error(Theatre::NamespaceNotFound)
44
+ end
45
+
46
+ it "#search_for_namespace should raise a NamespaceNotFound exception if a namespace two levels deep was not found" do
47
+ nm = Theatre::ActorNamespaceManager.new
48
+ lambda do
49
+ nm.search_for_namespace "/foo/bar"
50
+ end.should raise_error(Theatre::NamespaceNotFound)
51
+ end
52
+
53
+ describe '::normalize_path_to_array' do
54
+ it "should split a standard path properly" do
55
+ Theatre::ActorNamespaceManager.normalize_path_to_array("/foo/bar/qaz").should == [:foo, :bar, :qaz]
56
+ end
57
+
58
+ it "should split out Array()'d form of a String path properly" do
59
+ Theatre::ActorNamespaceManager.normalize_path_to_array(["/jay/thomas/phillips"]).should == [:jay,:thomas,:phillips]
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ describe "NamespaceNode" do
66
+
67
+ it "when registering a new namespace, the new NamespaceNode should be returned" do
68
+ node = Theatre::ActorNamespaceManager::NamespaceNode.new "foobar"
69
+ node.register_namespace_name("foobar").should be_instance_of(Theatre::ActorNamespaceManager::NamespaceNode)
70
+ end
71
+
72
+ it "should not blow away an existing callback when registering a new one with the same name" do
73
+ name = "blah"
74
+ node = Theatre::ActorNamespaceManager::NamespaceNode.new name
75
+ node.register_namespace_name name
76
+ before = node.child_named(name)
77
+ before.should be_instance_of(Theatre::ActorNamespaceManager::NamespaceNode)
78
+ node.register_namespace_name name
79
+ before.should eql(node.child_named(name))
80
+ end
81
+
82
+ describe '#register_namespace_name' do
83
+ it "should return the NamespaceNode" do
84
+ Theatre::ActorNamespaceManager::NamespaceNode.new("foo").register_namespace_name("bar").should \
85
+ be_instance_of(Theatre::ActorNamespaceManager::NamespaceNode)
86
+ end
87
+ end
88
+
89
+ end
90
+
91
+ describe "Valid namespace segments" do
92
+
93
+ include NamespaceHelper
94
+
95
+ describe "a valid namespace path" do
96
+
97
+ it "should require a namespace path start with a /" do
98
+ "/foo". should be_valid_actor_event_namespace
99
+ "foo". should_not be_valid_actor_event_namespace
100
+ "foo/bar".should_not be_valid_actor_event_namespace
101
+ end
102
+
103
+ it "should allow multiple namespace segments" do
104
+ "/foo_foo/bar".should be_valid_actor_event_namespace
105
+ end
106
+
107
+ it "should not allow a trailing forward slash" do
108
+ "/foo/bar/".should_not be_valid_actor_event_namespace
109
+ end
110
+
111
+ it "should not allow backslashes" do
112
+ '\foo'.should_not be_valid_actor_event_namespace
113
+ '\foo\bar'.should_not be_valid_actor_event_namespace
114
+ 'foo\bar'.should_not be_valid_actor_event_namespace
115
+ '\bar\\'.should_not be_valid_actor_event_namespace
116
+ end
117
+
118
+ it "should not allow weird characters" do
119
+ %w[ ! @ # $ % ^ & * ( ) { } | ' : ? > < - = ].each do |bad_character|
120
+ "/foo#{bad_character}foo/bar".should_not be_valid_actor_event_namespace
121
+ end
122
+ end
123
+
124
+ end
125
+ end