coupler 0.0.4-java → 0.0.6-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/Gemfile +7 -8
  2. data/Gemfile.lock +43 -24
  3. data/VERSION +1 -1
  4. data/coupler.gemspec +27 -31
  5. data/features/wizard.feature +2 -1
  6. data/lib/coupler.rb +2 -2
  7. data/lib/coupler/base.rb +4 -0
  8. data/lib/coupler/extensions/connections.rb +2 -12
  9. data/lib/coupler/extensions/jobs.rb +4 -2
  10. data/lib/coupler/extensions/projects.rb +1 -1
  11. data/lib/coupler/helpers.rb +9 -1
  12. data/lib/coupler/models.rb +8 -0
  13. data/lib/coupler/models/comparison.rb +5 -4
  14. data/lib/coupler/models/connection.rb +10 -1
  15. data/lib/coupler/models/import.rb +3 -7
  16. data/lib/coupler/models/transformer.rb +1 -1
  17. data/lib/coupler/runner.rb +1 -1
  18. data/lib/coupler/scheduler.rb +1 -1
  19. data/tasks/test.rake +8 -0
  20. data/test/functional/test_base.rb +17 -0
  21. data/test/functional/test_connections.rb +81 -0
  22. data/test/functional/test_imports.rb +76 -0
  23. data/test/{integration/extensions → functional}/test_jobs.rb +21 -12
  24. data/test/functional/test_matchers.rb +108 -0
  25. data/test/functional/test_projects.rb +67 -0
  26. data/test/functional/test_resources.rb +126 -0
  27. data/test/{integration/extensions → functional}/test_results.rb +20 -29
  28. data/test/functional/test_scenarios.rb +92 -0
  29. data/test/functional/test_transformations.rb +106 -0
  30. data/test/functional/test_transformers.rb +68 -0
  31. data/test/helper.rb +30 -20
  32. data/test/integration/test_transformation.rb +6 -1
  33. data/test/unit/models/test_common_model.rb +2 -2
  34. data/test/unit/models/test_comparison.rb +8 -8
  35. data/test/unit/models/test_connection.rb +2 -2
  36. data/test/unit/models/test_field.rb +2 -2
  37. data/test/unit/models/test_import.rb +9 -4
  38. data/test/unit/models/test_job.rb +2 -2
  39. data/test/unit/models/test_matcher.rb +2 -2
  40. data/test/unit/models/test_project.rb +2 -2
  41. data/test/unit/models/test_resource.rb +2 -2
  42. data/test/unit/models/test_result.rb +2 -2
  43. data/test/unit/models/test_scenario.rb +2 -2
  44. data/test/unit/models/test_transformation.rb +2 -2
  45. data/test/unit/models/test_transformer.rb +12 -2
  46. data/test/unit/test_base.rb +1 -14
  47. data/test/unit/test_data_uploader.rb +1 -1
  48. data/test/unit/test_database.rb +1 -1
  49. data/test/unit/test_helpers.rb +2 -2
  50. data/test/unit/test_import_buffer.rb +40 -38
  51. data/test/unit/test_logger.rb +1 -1
  52. data/test/unit/test_models.rb +1 -1
  53. data/test/unit/test_runner.rb +1 -1
  54. data/test/unit/test_scheduler.rb +1 -1
  55. data/webroot/public/css/style.css +7 -5
  56. data/webroot/public/js/jquery.dataTables.min.js +130 -574
  57. data/webroot/views/connections/new.erb +41 -3
  58. data/webroot/views/imports/new.erb +2 -0
  59. data/webroot/views/jobs/list.erb +25 -21
  60. data/webroot/views/projects/index.erb +23 -15
  61. data/webroot/views/resources/list.erb +1 -2
  62. data/webroot/views/resources/new.erb +2 -2
  63. data/webroot/views/resources/show.erb +5 -2
  64. data/webroot/views/scenarios/new.erb +1 -1
  65. data/webroot/views/transformations/new.erb +1 -1
  66. metadata +30 -44
  67. data/lib/coupler/config.rb +0 -128
  68. data/test/coupler/models/test_import.rb +0 -221
  69. data/test/factories.rb +0 -91
  70. data/test/integration/extensions/test_connections.rb +0 -80
  71. data/test/integration/extensions/test_imports.rb +0 -94
  72. data/test/integration/extensions/test_matchers.rb +0 -134
  73. data/test/integration/extensions/test_projects.rb +0 -82
  74. data/test/integration/extensions/test_resources.rb +0 -150
  75. data/test/integration/extensions/test_scenarios.rb +0 -88
  76. data/test/integration/extensions/test_transformations.rb +0 -113
  77. data/test/integration/extensions/test_transformers.rb +0 -80
  78. data/vendor/h2-1.3.154.jar +0 -0
@@ -3,7 +3,16 @@ module Coupler
3
3
  class Connection < Sequel::Model
4
4
  include CommonModel
5
5
 
6
- ADAPTERS = [%w{mysql MySQL h2 H2}]
6
+ ADAPTERS = [
7
+ {
8
+ :name => "mysql", :label => "MySQL",
9
+ :ignored_attributes => %w{path}
10
+ },
11
+ {
12
+ :name => "h2", :label => "H2",
13
+ :ignored_attributes => %w{host port username password database_name}
14
+ }
15
+ ]
7
16
 
8
17
  one_to_many :resources
9
18
 
@@ -209,14 +209,10 @@ module Coupler
209
209
  def validate
210
210
  super
211
211
 
212
- validates_presence :project_id
213
- if project_id
214
- # don't allow import to have the same name as an already existing resource
215
- if project.resources_dataset.filter(:name => name).count > 0
216
- errors.add(:name, "is already taken")
217
- end
212
+ validates_presence [:project_id, :name, :field_names, :primary_key_name]
213
+ if name && project_id
214
+ validates_unique [:project_id, :name]
218
215
  end
219
- validates_presence [:field_names, :primary_key_name]
220
216
  if field_names.is_a?(Array)
221
217
  validates_includes field_names, [:primary_key_name]
222
218
 
@@ -3,7 +3,7 @@ module Coupler
3
3
  class Transformer < Sequel::Model
4
4
  include CommonModel
5
5
 
6
- plugin :serialization, :marshal, :allowed_types
6
+ plugin :serialization, :marshal, :allowed_types, :code
7
7
 
8
8
  TYPES = %w{string integer datetime}
9
9
  EXAMPLES = {
@@ -51,7 +51,7 @@ module Coupler
51
51
 
52
52
  if success
53
53
  Coupler::Base.set(:running, true)
54
- say "Web server is up and running on http://#{settings.bind}:#{settings.port}"
54
+ puts "Web server is up and running on http://#{settings.bind}:#{settings.port}"
55
55
  if !options.has_key?(:trap) || options[:trap]
56
56
  trap("INT") do
57
57
  shutdown
@@ -37,7 +37,7 @@ module Coupler
37
37
  if !is_started?
38
38
  @loop = Thread.new do
39
39
  loop do
40
- sleep 30
40
+ sleep 10
41
41
  run_jobs
42
42
  end
43
43
  end
@@ -19,6 +19,14 @@ namespace :test do
19
19
  test.ruby_opts = %w{--debug}
20
20
  end
21
21
  task :integration => ['environment:test', 'db:purge', 'db:migrate', 'db:fake']
22
+
23
+ Rake::TestTask.new(:functional) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/functional/**/test_*.rb'
26
+ #test.verbose = true
27
+ test.ruby_opts = %w{--debug}
28
+ end
29
+ task :functional => ['environment:test', 'db:purge', 'db:migrate', 'db:fake']
22
30
  end
23
31
 
24
32
  begin
@@ -0,0 +1,17 @@
1
+ require 'helper'
2
+
3
+ module CouplerFunctionalTests
4
+ class TestBase < Coupler::Test::FunctionalTest
5
+ def test_index_when_no_projects
6
+ visit("/")
7
+ assert_equal 200, page.status_code
8
+ assert page.has_content?('Getting Started')
9
+ end
10
+
11
+ def test_redirect_when_projects_exist
12
+ project = Project.create(:name => 'foo')
13
+ visit("/")
14
+ assert_equal "/projects", current_path
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,81 @@
1
+ require 'helper'
2
+
3
+ module CouplerFunctionalTests
4
+ class TestConnections < Coupler::Test::FunctionalTest
5
+ def setup
6
+ super
7
+ @connections = []
8
+ @configs = {}
9
+ each_adapter do |adapter, config|
10
+ conn = new_connection(adapter, :name => "#{adapter} connection").save!
11
+ @connections << conn
12
+ @configs[adapter] = config
13
+ end
14
+ end
15
+
16
+ test "index" do
17
+ visit "/connections"
18
+ assert page.has_selector?('a[href="/connections/new"]')
19
+ end
20
+
21
+ test "new" do
22
+ visit "/connections/new"
23
+ assert page.has_selector?("form[action='/connections']")
24
+ assert page.has_selector?("select[name='connection[adapter]']")
25
+ %w{name host port username password}.each do |name|
26
+ assert page.has_selector?("input[name='connection[#{name}]']")
27
+ end
28
+ end
29
+
30
+ each_adapter do |adapter, config|
31
+ attribute(:javascript, true)
32
+ test "successfully creating #{adapter} connection" do
33
+ attributes = config.merge(:name => 'foo')
34
+
35
+ visit "/connections/new"
36
+ find("#adapter").find("option[value='#{adapter}']").select_option
37
+ sleep 1 # wait for animations
38
+ attributes.each_pair do |name, value|
39
+ fill_in "connection[#{name}]", :with => value
40
+ end
41
+ click_button "Submit"
42
+
43
+ connection = Connection[:name => 'foo']
44
+ assert connection
45
+
46
+ assert_equal "/connections", page.current_path
47
+ end
48
+ end
49
+
50
+ test "failing to create connection" do
51
+ attributes = @configs['mysql'].merge(:name => nil)
52
+
53
+ visit "/connections/new"
54
+ select 'MySQL', :from => "connection[adapter]"
55
+ attributes.each_pair do |name, value|
56
+ fill_in "connection[#{name}]", :with => value
57
+ end
58
+ click_button "Submit"
59
+
60
+ assert page.has_content?("Name is not present")
61
+ end
62
+
63
+ test "show" do
64
+ @connections.each do |conn|
65
+ visit "/connections/#{conn.id}"
66
+ assert page.has_selector?("table.show")
67
+ end
68
+ end
69
+
70
+ attribute(:javascript, true)
71
+ test "destroy" do
72
+ visit "/connections"
73
+ find('span.ui-icon-trash').click
74
+ a = page.driver.browser.switch_to.alert
75
+ a.accept
76
+
77
+ assert_equal '/connections', page.current_path
78
+ assert_nil Models::Connection[@connections[0].id]
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,76 @@
1
+ require 'helper'
2
+
3
+ module CouplerFunctionalTests
4
+ class TestImports < Coupler::Test::FunctionalTest
5
+ def setup
6
+ super
7
+ @project = Project.create(:name => 'foo')
8
+ end
9
+
10
+ attribute(:javascript, true)
11
+ test "upload" do
12
+ visit "/projects/#{@project.id}/resources/new"
13
+ find('label[for="resource-type-csv"]').click
14
+ attach_file('data', fixture_path('people.csv'))
15
+
16
+ assert_equal "/projects/#{@project.id}/imports/upload", page.current_path
17
+ assert page.has_selector?('input#name')
18
+ end
19
+
20
+ attribute(:javascript, true)
21
+ test "upload with no headers" do
22
+ visit "/projects/#{@project.id}/resources/new"
23
+ find('label[for="resource-type-csv"]').click
24
+ attach_file('data', fixture_path('no-headers.csv'))
25
+
26
+ assert_equal "/projects/#{@project.id}/imports/upload", page.current_path
27
+ assert page.has_selector?('input#name')
28
+ end
29
+
30
+ attribute(:javascript, true)
31
+ test "create with no issues" do
32
+ visit "/projects/#{@project.id}/resources/new"
33
+ find('label[for="resource-type-csv"]').click
34
+ attach_file('data', fixture_path('people.csv'))
35
+
36
+ click_button('Begin Importing')
37
+ assert_match %r{^/projects/#{@project[:id]}/resources/\d+$}, page.current_path
38
+ end
39
+
40
+ attribute(:javascript, true)
41
+ test "create with invalid import" do
42
+ visit "/projects/#{@project.id}/resources/new"
43
+ find('label[for="resource-type-csv"]').click
44
+ attach_file('data', fixture_path('people.csv'))
45
+
46
+ fill_in('name', :with => '')
47
+ click_button('Begin Importing')
48
+
49
+ assert page.has_selector?("div.errors")
50
+ end
51
+
52
+ attribute(:javascript, true)
53
+ test "create with duplicate keys redirects to edit" do
54
+ visit "/projects/#{@project.id}/resources/new"
55
+ find('label[for="resource-type-csv"]').click
56
+ attach_file('data', fixture_path('duplicate-keys.csv'))
57
+ click_button('Begin Importing')
58
+
59
+ assert find("h2").has_content?("Duplicate Keys")
60
+ assert_match %r{^/projects/#{@project.id}/imports/\d+/edit$}, page.current_path
61
+ end
62
+
63
+ attribute(:javascript, true)
64
+ test "update import with duplicate keys" do
65
+ visit "/projects/#{@project.id}/resources/new"
66
+ find('label[for="resource-type-csv"]').click
67
+ attach_file('data', fixture_path('duplicate-keys.csv'))
68
+ click_button('Begin Importing')
69
+
70
+ find('input[name="delete[2][]"][value="1"]').click
71
+ click_button('Submit')
72
+
73
+ assert_match %r{^/projects/#{@project[:id]}/resources/\d+$}, page.current_path
74
+ end
75
+ end
76
+ end
@@ -1,7 +1,7 @@
1
1
  require 'helper'
2
2
 
3
- module TestExtensions
4
- class TestJobs < Coupler::Test::IntegrationTest
3
+ module CouplerFunctionalTests
4
+ class TestJobs < Coupler::Test::FunctionalTest
5
5
  def self.startup
6
6
  super
7
7
  conn = new_connection('h2', :name => 'foo')
@@ -27,25 +27,34 @@ module TestExtensions
27
27
  })
28
28
  end
29
29
 
30
+ attribute(:javascript, true)
30
31
  test "jobs" do
31
- job = Job.create!(:name => 'transform', :status => 'scheduled', :resource => @resource)
32
- get "/jobs"
33
- assert last_response.ok?
32
+ visit "/projects/#{@project.id}/resources/#{@resource.id}"
33
+ click_button "Transform now"
34
+ a = page.driver.browser.switch_to.alert
35
+ a.accept
36
+
37
+ visit "/jobs"
38
+ assert page.has_selector?("table.list tbody tr")
34
39
  end
35
40
 
41
+ # Using Rack::Test directly here for JSON tests
36
42
  test "count" do
37
43
  scheduled_job = Job.create!(:name => 'transform', :status => 'scheduled', :resource => @resource)
38
44
  completed_job = Job.create!(:name => 'transform', :status => 'done', :resource => @resource, :completed_at => Time.now)
39
- get "/jobs/count"
40
- assert last_response.ok?
41
- assert_equal "1", last_response.body
45
+ page.driver.get "/jobs/count"
46
+ assert_equal "1", page.driver.response.body
42
47
  end
43
48
 
44
49
  test "progress" do
45
- job = Job.create!(:name => 'transform', :status => 'scheduled', :resource => @resource, :total => 200, :completed => 54)
46
- get "/jobs/#{job.id}/progress"
47
- assert last_response.ok?
48
- result = JSON.parse(last_response.body)
50
+ visit "/projects/#{@project.id}/resources/#{@resource.id}"
51
+ click_button "Transform now"
52
+ assert_equal "/projects/#{@project.id}/resources/#{@resource.id}", page.current_path
53
+
54
+ job = @resource.scheduled_jobs.first
55
+ job.update(:total => 200, :completed => 54)
56
+ page.driver.get "/jobs/#{job.id}/progress"
57
+ result = JSON.parse(page.driver.response.body)
49
58
  assert_equal({'total' => 200, 'completed' => 54}, result)
50
59
  end
51
60
  end
@@ -0,0 +1,108 @@
1
+ require 'helper'
2
+
3
+ module CouplerFunctionalTests
4
+ class TestMatchers < Coupler::Test::FunctionalTest
5
+ def self.startup
6
+ super
7
+ conn = new_connection('h2', :name => 'foo')
8
+ conn.database do |db|
9
+ db.create_table!(:foo) do
10
+ primary_key :id
11
+ String :foo
12
+ String :bar
13
+ end
14
+ db[:foo].insert({:foo => 'foo', :bar => 'bar'})
15
+ db[:foo].insert({:foo => 'bar', :bar => 'foo'})
16
+ end
17
+ end
18
+
19
+ def setup
20
+ super
21
+ @connection = new_connection('h2', :name => 'foo').save!
22
+ @project = Project.create!(:name => 'foo')
23
+ @resource = Resource.create!(:name => 'foo', :project => @project, :table_name => 'foo', :connection => @connection)
24
+ @scenario = Scenario.create!(:name => 'foo', :project => @project, :resource_1 => @resource)
25
+ end
26
+
27
+ test "new" do
28
+ visit "/projects/#{@project.id}/scenarios/#{@scenario.id}/matchers/new"
29
+ assert_equal 200, page.status_code
30
+ end
31
+
32
+ test "new with non existant project" do
33
+ visit "/projects/8675309/scenarios/#{@scenario.id}/matchers/new"
34
+ assert_equal "/projects", page.current_path
35
+ assert page.has_content?("The project you were looking for doesn't exist")
36
+ end
37
+
38
+ test "new with non existant scenario" do
39
+ visit "/projects/#{@project.id}/scenarios/8675309/matchers/new"
40
+ assert_equal "/projects/#{@project.id}/scenarios", page.current_path
41
+ assert page.has_content?("The scenario you were looking for doesn't exist")
42
+ end
43
+
44
+ attribute(:javascript, true)
45
+ test "successfully creating matcher for self-linkage" do
46
+ visit "/projects/#{@project.id}/scenarios/#{@scenario.id}/matchers/new"
47
+ click_link("Add comparison")
48
+ select('foo', :from => "lhs_value_select")
49
+ find('span.ui-button-text', :text => 'Add').click
50
+ click_button('Submit')
51
+ assert_equal "/projects/#{@project.id}/scenarios/#{@scenario.id}", page.current_path
52
+
53
+ assert @scenario.matcher
54
+ end
55
+
56
+ attribute(:javascript, true)
57
+ test "edit" do
58
+ foo = @resource.fields_dataset[:name => 'foo']
59
+ bar = @resource.fields_dataset[:name => 'bar']
60
+ matcher = Matcher.create!({
61
+ :scenario => @scenario,
62
+ :comparisons_attributes => [{
63
+ 'lhs_type' => 'field', 'raw_lhs_value' => foo.id, 'lhs_which' => 1,
64
+ 'rhs_type' => 'field', 'raw_rhs_value' => bar.id, 'rhs_which' => 2,
65
+ 'operator' => 'equals'
66
+ }]
67
+ })
68
+ visit "/projects/#{@project.id}/scenarios/#{@scenario.id}/matchers/#{matcher.id}/edit"
69
+ click_link("Delete")
70
+
71
+ click_link("Add comparison")
72
+ find("#lhs_value_select").select("bar")
73
+ find("#rhs_value_select").select("foo")
74
+ find('span.ui-button-text', :text => 'Add').click
75
+ click_button('Submit')
76
+ assert_equal "/projects/#{@project.id}/scenarios/#{@scenario.id}", page.current_path
77
+
78
+ assert_equal 1, @scenario.matcher.comparisons_dataset.count
79
+ end
80
+
81
+ test "edit with non existant matcher" do
82
+ visit "/projects/#{@project.id}/scenarios/#{@scenario.id}/matchers/8675309/edit"
83
+ assert_equal "/projects/#{@project.id}/scenarios/#{@scenario.id}", page.current_path
84
+ assert page.has_content?("The matcher you were looking for doesn't exist")
85
+ end
86
+
87
+ attribute(:javascript, true)
88
+ test "delete" do
89
+ pend "This fails and I don't know why"
90
+ field = @resource.fields_dataset[:name => 'foo']
91
+ matcher = Matcher.create!({
92
+ :scenario => @scenario,
93
+ :comparisons_attributes => [{
94
+ 'lhs_type' => 'field', 'raw_lhs_value' => field.id, 'lhs_which' => 1,
95
+ 'rhs_type' => 'field', 'raw_rhs_value' => field.id, 'rhs_which' => 2,
96
+ 'operator' => 'equals'
97
+ }]
98
+ })
99
+ visit "/projects/#{@project.id}/scenarios/#{@scenario.id}"
100
+ link = page.driver.browser.find_element(:link_text, "Delete")
101
+ link.click
102
+ a = page.driver.browser.switch_to.alert
103
+ a.accept
104
+ assert_equal 0, Models::Matcher.filter(:id => matcher.id).count
105
+ assert_equal "/projects/#{@project.id}/scenarios/#{@scenario.id}", page.current_path
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,67 @@
1
+ require 'helper'
2
+
3
+ module CouplerFunctionalTests
4
+ class TestProjects < Coupler::Test::FunctionalTest
5
+ test "projects" do
6
+ visit "/projects"
7
+ assert_equal 200, page.status_code
8
+ end
9
+
10
+ test "create project" do
11
+ visit "/projects/new"
12
+ assert_equal 200, page.status_code
13
+
14
+ within('form[action="/projects"]') do
15
+ fill_in("Name", :with => "foo")
16
+ fill_in("Description", :with => "foo bar")
17
+ click_button("Submit")
18
+ end
19
+
20
+ assert_match %r{/projects/\d+}, page.current_path
21
+ assert page.has_content?("Project successfully created")
22
+ end
23
+
24
+ test "showing invalid project when projects exist" do
25
+ visit "/projects/8675309"
26
+ assert_not_equal "/projects/8675309", page.current_path
27
+ assert page.has_content?("The project you were looking for doesn't exist")
28
+ end
29
+
30
+ test "edit project" do
31
+ project = Project.create(:name => 'foo')
32
+ visit "/projects/#{project.id}/edit"
33
+
34
+ within("form[action='/projects/#{project.id}']") do
35
+ fill_in("Name", :with => "bar")
36
+ fill_in("Description", :with => "bar foo")
37
+ click_button("Submit")
38
+ end
39
+
40
+ assert_match %r{/projects/\d+}, page.current_path
41
+ end
42
+
43
+ attribute(:javascript, true)
44
+ test "delete" do
45
+ project = Project.create(:name => 'foo')
46
+ visit "/projects"
47
+ find('button.delete-project').click
48
+ find('#yes-button').click
49
+ assert_equal "/projects", page.current_path
50
+ #assert_nil Project[:id => project.id]
51
+ end
52
+
53
+ attribute(:javascript, true)
54
+ test "delete with versions" do
55
+ project = Project.create(:name => 'foo')
56
+ visit "/projects"
57
+ find('button.delete-project').click
58
+ find('#nuke').click
59
+ yes = find('#yes-button')
60
+ yes.click
61
+ yes.click
62
+ assert_equal "/projects", page.current_path
63
+ assert_nil Project[:id => project.id]
64
+ assert_nil Database.instance[:projects_versions][:current_id => project.id]
65
+ end
66
+ end
67
+ end