coupler 0.0.4-java → 0.0.6-java

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 (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